-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Allow significant indentation syntax #7083
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
Conversation
There was a problem hiding this 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:
- Separate subject from body with a blank line
- When fixing an issue, start your commit message with
Fix #<ISSUE-NBR>:
- Limit the subject line to 72 characters
- Capitalize the subject line
- Do not end the subject line with a period
- Use the imperative mood in the subject line ("Add" instead of "Added")
- Wrap the body at 80 characters
- Use the body to explain what and why vs. how
adapted from https://chris.beams.io/posts/git-commit
Have an awesome day! ☀️
Part of this PR is a reformat of the dotc compiler code base which makes the rewrite steps outlined above invariant on this code. I.e. if one starts with this codebase, rewriting to indentation based and then rewriting back to old syntax will yield exactly the code one started with. |
A long time ago as a kid in the early 1980ies, I came across someone using a Lisp machine. (These had colour that could be turned by 90degrees). They used 3 chars to indent their code. I asked why? They told me 2 is too little and 4 is too much. It actually makes sense. With 2 you can't see the identation, and 4 is a little too much. |
Same comment as with the previous PR: I personally find this harder to read and less pleasant. Indentation-based languages make me crazy, particularly in how tweaks to code can often lead to unintended semantic changes if you miss changing some indentation. It's fine for scripting, but terrible for serious long-lived code. I'd forbid it in codebases I control. And honestly, it feels like chasing a fad to me. Yes, Python is indentation-based -- but JavaScript and its cousins, which are the other languages I mainly see as feeders these days (and which seems to be an easier entree into Scala, since JS teaches a measure of FP) are largely brace-based. And we still get a lot of people coming in from Java -- I believe that all of the people I've been training up in Scala recently come from a Java background. Python gets all the hype, but braces are still utterly common in many peoples' habits. I'll also note: I've spent much of the past year reassuring folks all over the place that Scala 3 isn't going to be that different -- that routine application code will look and feel pretty close to existing code -- to calm nerves. When I talk to people in the community, I still see a lot of terror that we're going to screw this up and split the community a la Python: the majority of people I talk to (especially engineering managers) care far more that Scala 3 remains decently similar to Scala 2, and care much less about the new and cool stuff. IMO, this invalidates that argument: the resulting code looks dramatically different across the board. I think that's going to generally decrease folks' confidence in upgrading to Scala 3, so I think it's a bad idea strategically... |
I'd like to try this. I use python (and js, and a bunch of other things) alot and recently converted a python project to dotty. Can this get into 0.18 to be released in Aug (I hope)? Can it be applied file by file? I don't have time to convert existing files to this syntax in my codebase. |
@odersky I have some sympathy for this plan but I strongly suggest this be opt-in via a different file extension or something similarly extreme. It is after all effectively a totally different syntax for Scala in the same way that ReasonML is a different syntax for OCaml. |
Some initial thoughts:
All in all, I'm not sure if this is a good idea or not, but it's intriuging. One worry I have is that a compromise syntax that tries to mix elements of old and new is likely worse than either. |
I like the idea of a different file extension. |
I'm looking at this PR via mobile. The braces-free code is horrendous. |
I feel like the big picture requires more statistics. Here are the results of StackOverflow's 2019 survey. The majority clearly tends to perfer braces. But we may want to look at more recent trends and prefer more loved languages to legacy ones. In that case, look at this graph: Notice how Rust is the most loved one! Surely, it's because of its amazing brace-based syntax, isn't it? Just like Python's indentation makes it the "most widely taught language". 😃 In the end, the popularity of Python doesn't seem like a compelling argument in favor of indentation-based syntax. |
I personally find the indent-based more difficult to read: def Run(ch: Char, n: Int): Run =
if n <= MaxCached && ch == ' ' then
spaces(n)
else if n <= MaxCached && ch == '\t' then
tabs(n)
else
new Run(ch, n)
val Zero = Run(' ', 0) In the above, Maybe keep |
I thought python was funky with its space significant syntax but I'm Ok with it now. I know for me, I have to try it on something for a bit to see if it works. I get tired of Perhaps we can get rid of "then" per the above comment. I have alot of folds, maps, flatMaps, for and other things on lists that generate a bunch of braces, so I'm curious if it helps with these constructs. My only concern on trying this out is that if editors don't support it, how can I really try it? |
I'm on a team of mixed-experience-level programmers (both generally and with scala). Earlier this year I did an informal survey on development pain points in our codebase, and one very common response was "It's hard to figure out what things do at a glance". I'm concerned that moving to whitespace-sensitivity, even optionally, would be a detrimental impact for scala. I think indentation makes sense for languages with much simpler syntax than scala. Python and markdown a great example: they both are incredibly regular, and the syntax has very few variations. For scala, we get a lot of flexibility in how code looks because of things like infix syntax and implicits. I think that even now, keeping the "novelty budget" down will benefit the community more. |
I was wondering what the impact (negative or positive) would be for visually impaired programmers? Maybe we can ask someone who worked on SCP-016. |
One of the key points in making dotty adoption possible is the possibility to migrate existing code to scala3 syntax. Will it be possible to migrate current code to this new syntax using tools like scalafix? |
What problem is this solving? |
squashed to a single commit on top of #7024. |
Chiming in just to mention that this will effectively make the parser in Scalameta useless, and with it we’ll also lose Scalafmt and Scalafix. I dislike the syntax, but I can get over it, but I’m mostly worried about:
|
Yes, maybe we can still find a way to simplify this. The current syntax is stated in a way that is purely lexical: class C: since without it the header ends in an identifier, which is not one of the tokens after which That leaves optimize {
...
} since that would mix indents and braces, and indents would be turned off inside the outer braces. You'd want to write optimize:
... instead. But maybe we can restrict suffix
We definitely need more experimentation to come up with a recommendation here.
In fact, the proposal allows for an optional end marker. |
I think you are going to find that with the new control syntax and significant indentation, Scala syntax is just as simple as Python syntax, if not simpler. In fact a significant part of the complexity in current Scala syntax comes from the over-use of parentheses and braces (e.g. how parens and braces are used in for expressions). |
This is already possible, using just the dotty compiler with the |
One argument in favor of indentation that I forgot to mention is that I expect it to be easier to teach. Here's a strong argument along these lines from Chris Okasaki: http://okasaki.blogspot.com/2008/02/in-praise-of-mandatory-indentation-for.html In fact, the plan is that, starting next month we will teach a full course of functional programming to EPFL students using Scala 3 with the new syntax. That's basically the first two MOOCs in the Scala specialization. After that we'll have hopefully more data that helps us decide what to do with the idea. |
There is so much emotionally laden bike shedding about the most arbitrary of things like keywords and/or code layout. Because code is data, shouldn't how to name particular language keywords (consider the recent debate around "given") and then how to format/layout the code itself (maybe even one way for reading/scanning, and another for editing) be a user-specific configured decoder/encoder applied to said data? Won't TASTY help ease some of this by allowing an intelligent editor display to the user whatever hell keywords and/or format the particular user wants via their own customized configuration, and then TASTY (as a form of a DOM) is what is actually stored? If not, then what would be needed to get Scala source code into a standardized serialized format which could work with a user-specific configured decoder/encoder? We spend so freakin much time separating interface from implementation in two entirely different paradigms (OOP and FP), why don't we do it here? Attempting to force agreement (like on "given") versus enabling intelligent personalized customization (use whatever you like given you take responsibility for retaining parsing coherence) seems like it would eliminate so many of these issues...which are like from like 5 decades ago. Do you know what would be fun? To bring back the requirement for semicolons. And don't even get me started about code required to be file system based versus using far more modern database schema tools... /rant Bike-shed away lots of time that could be productively spent MANY other far more valuable ways. |
For better comparability, here's the enum IndentWidth:
case Run(ch: Char, n: Int)
case Conc(l: IndentWidth, r: Run)
def <= (that: IndentWidth): Boolean =
this match
case Run(ch1, n1) =>
that match
case Run(ch2, n2) => n1 <= n2 && (ch1 == ch2 || n1 == 0)
case Conc(l, r) => this <= l
case Conc(l1, r1) =>
that match
case Conc(l2, r2) => l1 == l2 && r1 <= r2
case _ => false
def < (that: IndentWidth): Boolean = this <= that && !(that <= this)
override def toString: String =
this match
case Run(ch, n) =>
val kind = ch match
case ' ' => "space"
case '\t' => "tab"
case _ => s"'$ch'-character"
val suffix = if n == 1 then "" else "s"
s"$n $kind$suffix"
case Conc(l, r) =>
s"$l, $r"
object IndentWidth:
private inline val MaxCached = 40
private val spaces = IArray.tabulate(MaxCached + 1):
new Run(' ', _)
private val tabs = IArray.tabulate(MaxCached + 1):
new Run('\t', _)
def Run(ch: Char, n: Int): Run =
if n <= MaxCached && ch == ' ' then
spaces(n)
else if n <= MaxCached && ch == '\t' then
tabs(n)
else
new Run(ch, n)
end Run
val Zero = Run(' ', 0) Of course that's just one first example, and as such does not tell us much. We'll do a lot more experimentation before coming up wth a recommendation. |
Thank you Martin for the interesting article about the benefits of mandatory indentation. I see the value of enforcing cleaner code and removing the clutter of braces (and Some remarks to improve the learning experience: In my opinion, Scala would be easier to learn if parameters were all written between parentheses, like this: private val spaces = Array.tabulate(MaxCached + 1)(
new Run(' ', _)
)
optimize (
...
)
Following this idea, I think it would be better for colons not to introduce blocks. That is, keep them for typing only. If a character is needed e.g. for declaring a class, declaration ::= [scope] {modifier} declarationKind name {parameterList} "=" definition
declarationKind ::= "def" | "class" | ...
definition ::= expression | "\n" indent {expression} (outdent | eof) |
As an exploration, using enum Color = case Red, Green, Blue Compare that to: enum Color {
case Red, Green, Blue
} |
A strong argument against significant indentation is that it's worse for the blind/visually impaired or people who use screen readers to program. White spaces aren't easy to distinguish with screen readers and most of the screen readers read each single space individually which is frustrating or totally ignore it. Relevant experiences:
edit: I think tooling could solve this problem, with editors/IDEs that integrate somehow with screen readers. Also, I'm not sure how screen readers deal with nested braces but as far as I read online, it seems to be an easier thing to deal with for them. If someone can reach someone with hands on experience it might give a better insight |
This is good innovative thinking. Significant indentation results in cleaner code. However, even if it were made the only way to define the grammar, it would not be worth the adoption friction. |
This is understandably very controversial, but if I get what sir Odersky is aiming for, it must be the fact that Python is recognizable almost everywhere now. Perhaps his current end goal is to write a language that is significantly more accessible to anyone for the sake of its future and its community. More accessible in terms of people who have less time to devote to studying it, and in terms of the growing mindset in various domains that there's always 1-2 languages you could go to. When data science started becoming a thing, the jerk reaction of people who want to get into it is to check if Python could do it since it was widely known even by non-developers. When educators realized that Java isn't necessary to be taught for introductory computer science courses, they turned to Python--it was a lot easier, simpler, and more understandable to aspiring engineers. Hell, even business, humanities, and social sciences people know about Python, even if they just heard it ("that's like Excel too, right? where you can write formulas?"). Frankly speaking, Scala would just usually come up when talking about big data and tools like Apache Spark. Even my peers are surprised when I tell them I use Scala for web development ("I didn't know you can use it there???"). A language that could be taught (1) to the most clueless students and (2) by teachers who don't need very proficient Scala-fu to do so (even scala's type system is crazy even for someone who uses scala for work) is very, very useful and impactful. If we will say, for example, that JavaScript is pretty much at the top in terms of language popularity in the annual Stack Overflow survey, and it's using the braces syntax, we would be forgetting two essential elements to its popularity: (1) web development is basically everywhere, and (2) web development is THE most accessible domain to specialize in as an IT professional. I mean, why should I enter the realm of embedded software development or system administration, when I could already freelance as a web developer to dozens of potential clients who need their websites made, even just via Wordpress or Shopify? While all the arguments for and against this proposal are all highly technical in nature (and safe to say, biased as people who already learned and currently code in Scala), I think we should remember that the learning curve of Scala as a language is much, much steeper compared to most high-level languages. Personally, it's hard to see Scala growing as a community when outsiders and potential newcomers only chalk it up as an unnecessarily complex (not flexible) language. People think Python is very flexible (not complex) because the domains it can cover vary widely, and people can pick up the learning quickly. If this was a simple popularity contest, Scala wouldn't come close (we got a huge boost in, like, big data because Spark. Do we count the Play framework, or was it because of piggybacking on Java Play?). I would very much rather read this proposal as one's way to try to reach a wider audience for the language. It just so happens that Python, given the domains it has reached (data science, web development, physics, business intelligence, protein-folding, machine learning, etc), is the most popular one among developers and non-developers alike. At the end of the day, if only a niche community of developers talk about Scala, then how should we expect Scala to expand to domains that non-developers dominate, but could use a hell lot of tech help? With all that said, I'm asking myself if this indentation-based syntax going to help the community in the long term, or if there are better ways. I'm always at a loss for words whenever I'm stuck in a Scala programming issue, because there never seems to be clearer documentation and noticeable community activity that could reassure me a bit that someone could help me out. This is me as someone who's only been using Scala for 1 year, self-taught. Using Silhouette for your Scala app's authentication? Good luck trying to implement that if you don't have a good grasp of Scala interfaces/classes. That took me a month to get it working, on my own. Did I ask for help online? Yes. Personally, I don't want Scala to grow old as just another domain-specific language (most likely in big data). It's a well-designed language for general purpose computing, and I think we should make sure it remains to be so. To me, this proposal seems like a jab at that pain point both as a language and as a community moving forward. We should remember that people saying something is just plain ugly isn't an argument at all. Scala was written with the brace-style syntax ingrained in it, and if the indentation-style syntax would help us get closer to lowering the learning curve, and therefore increasing accessibility, then the question we should really be asking are does this help us achieve those and if the answer to that is yes to a considerable degree, how to redesign the Scala syntax in terms of this other style without compromising on the power and flexibility of the language. Those mind-numbing underscores and long chains of methods shouldn't leave. Neither does the complex type system with all those From a guy who uses both Scala and Python professionally |
@nafg it may open the door for eliding Really depends on how far one goes with the experiment. If it's "just" the following, then I'd say the experiment doesn't go far enough, and isn't really worth pursuing.
I don't particularly care one way or the other about braces vs. whitespace, but I do care a lot about repeating keywords all over the place that the compiler could infer for me, provided the syntax gave the necessary clues. service.update(entity).map:
Right(x) => Ok(toJson(x))
Left(err) => BadRequest(err) service.update(entity).map {
case Right(x) => Ok(toJson(x))
case Left(err) => BadRequest(err)
} |
@godenji what problem would be solved by making scala's syntax nicer (granting the premise for the moment) There are lots of things that are nice but I think we need to prioritize solving problems unless there aren't any big ones (which is not the case). I was thought someone might say, the problem of scala's limited popularity. But either way, maybeProblem match {
case None =>
println("Wouldn't the energy be better spent on solving problems that exist or are imminent?")
case Some(problem) =>
getAllPotentialSolutionsFor(problem)
.sortBy(s => (s.likelihoodToActuallySolve, -s.effortNeeded))
.foreach(s => println(s"Are we trying $s?")
} |
I am skeptical that this would be an improvement. It might be better for early learning in a protected environment, but it will be worse for learning to mastery because you'll have to train yourself to recognize two visual patterns to recognize for a bunch of common stuff (and they're not that visually distinct, which will make the task harder). I also don't think that Python's popularity is terribly much affected by significant whitespace. The most loved language by a large margin is Rust, and it has braces all over the place. Kotlin's high and it's full of braces. TypeScript too. Furthermore, Python's popularity and ranking on most-loved-language has been growing year after year, and the whitespace thing hasn't changed a bit. I don't think it's the whitespace. If it's not the whitespace, what is it in Python's case? Since the language hasn't changed much, I can only conclude that it must be mostly ecosystem. You can get things done with a minimum of fuss, mostly using libraries that are designed with that goal in mind. Technically, I think the proposal introduces some syntactic ambiguity. For instance, this is currently valid Scala but would be ambiguous: def foo(i: Int)(j: Int) = i + j
val partialAp = foo(5):
Int => Int I haven't thought of anything that would compile silently with altered behavior, but it does suggest that using |
@nafg Take a look at the previous round of PR #2491. As motivations it lists:
and for impediments:
#2491 does open with:
Curiously, however, languages like OCaml, F#, and Haskell do not seem to use offside rule for if expression or passing blocks and lambdas. Instead what they do is use parenthesis or curly braces. let results = List.choose (fun elem ->
match elem with
| elem when isCapitalized elem -> Some(elem + "'s")
| _ -> None) listWords use if GtkBase.Object.is_a obj cls then
fun _ -> f obj
else begin
eprintf "Glade-warning: %s expects a %s argument.\n" name cls;
raise Not_found
end or use calcBmis :: (RealFloat a) => [(a, a)] -> [a]
calcBmis xs = [bmi w h | (w, h) <- xs]
where bmi weight height = weight / height ^ 2 There is offside rule in Haskell, but it is started with the layout keywords Verbose Syntax lists a side-by-side comparison of lightweight vs verbose syntax for F#. In their case, it seems the lightweightness mostly comes from being able to omit the end markers like |
@eed3si9n those are motivations and benefits however my question is, is there a problem that it is solving? Because if not I wish we would prioritize solving problems over enhancements. And if yes, then we should frame it that way, because it helps to see where it fits into the bigger picture (cf. the 5 whys technique). I can reframe some of the items on your as problems, however I'm not sure if it makes sense to: Cleaner typography: Does the more noisy syntax cause any problems? If so what are they? Of course there are a lot of premises in this that are debatable but that is not my objective. What I'm trying to do is to get the "pro" team to crystalize and communicate clearly their reasoning behind investing effort into this as it fits into the bigger picture. Again, I don't want to make it sound like I'm demanding anything, no one owes me this of course. I just feel like I'm asking a simple question but I'm having a hard time getting it understood. |
@nafg The main problem this is potentially solving is that Scala is not as accessible as it could be for beginners and casual users. For me, that is an important aspect. I am saying potentially since right now this is just a hypothesis. We need a lot more experience to back that up, and the hypothesis might turn out to be wrong in the end. But now is not the right time for me to discuss this. Everything I write would be pre-mature.
I thought I had debunked that in my comment? People don't have a problem to train themselves to recognize if (x < 0)
x = -x and if (x < 0) {
x = -x
} as equivalent. The core of significant indentation is to also establish that equivalence for multi-statement expressions.
These are different audiences. Rust is for systems programmers and needs very high expertise. Kotlin and Typescript are also specialized languages. Python is great for beginners and casual users. That's the audience I am after. Indentation is not a panacea of course, but maybe it removes one impediment to easy access. There are other impediments for sure (a big one is over-complicated libraries), and we will have to work on all of them. |
@odersky thanks. Not sure where to ask since it's now perhaps a tangent but what do you think of having a high level google doc or something where we outline all the priorities and how they fit in with each other etc.? |
I think that would be very useful. Right now the closest is we have is https://dotty.epfl.ch/docs/reference/overview.html This needs to be periodically updated, of course. Do you think we should have another format as well? |
I would strongly advise against using single colons as the delimiter between the declaration and the body of the class/object/enum implementation. In Scala, the single colon is mostly used as the symbol for type annotations, both in class fields, method parameters, method return types, etc. To use the same symbol for a different purpose (begin the body of the implementation) would complicate the language. IMHO, the syntactic complexity of Scala does not stem from it using many diverse symbols, but in using same symbols for different purposes. That is a cognitive burden to the reader, who has to "parse" a symbol by its surrounding. It is similar to the underscore def fili[Kili[_]](x: Int): String = x match {
case b: Ori[_] => "yes"
case _ => "no"
} Perhaps, it could make more sense to use the equals |
A side-note regarding the use of colons: In Kotlin, the colon is also used within a class declaration as the symbol to represent inheritance relations between classes and interfaces, with commas to separate multiple interfaces. This may be a reasonable overload of the colon, to denote both typing and subtyping, and may be easier than the |
@bishabosha Do you have code where the exception happens? I could not reproduce (I tried it with #7114). |
@diesalbla Your point has been answered in #7083 (comment) May I ask what are your thoughts on having also the tokens |
@odersky - It took me close to a year to be able to read if (foo)
x = -x and if (foo) {
x = -x
} equally quickly, and with the same ability to catch errors. Maybe other people are different, but I think the premise is incorrect that because someone can immediately understand that the two are equivalent, said individual has mastery at that point. Rather, I think conceptual mastery is immediate, but the individual still has the skills of a beginner. Reading code then takes more effort and attention, and we only have so much effort and attention to give. Sometimes it's worth it, e.g. it takes time to learn to read |
@odersky Could you ellaborate as to what those differences are? To give a simple example, I can see the following as being similar: trait A { def x: Int }
// val-def
val b = new A { val x = 13 }
def c(y: Int): A = new A { val x = y + 1 }
// object-class
object B extends A { val x = 13 }
class C(y: Int) extends A { val x = y + 1 } In other words, an object is like a label for an anonymous instance of the super-trait, and a class is like a label for a function to create objects (or like a template of objects). Apart of the difference that One benefit of this syntax, using the trait A { def x: Int }
// val-def
val b = new A { val x = 13 }
def c(y: Int): A = new A { val x = y + 1 )
// object-class
object B: A = { val x = 13 }
class C(y: Int): A = { val x = y + 1} |
If we're going to do this, I propose that the ellipsis be a indention-based-block opener, and that every case where you don't use it is a case of ellipsis elision. This gives a nearly trivial translation between (unelided) braced and unbraced styles. For example, here's a bit of code I wrote recently: class Ps(_roost: Path) extends FourFlock[P, Ps](_roost) { self =>
lazy val allEs = all.map(_.collect{ case e: E => e })
lazy val allMs = all.map(_.collect{ case m: M => m })
protected def justWrap(path: Path) = P(path)
def filtered(pred: P => Boolean): Ps = new Ps(roost) with ProxyFlock[P, P, Ps] {
protected def flocker = self
protected def proxyOf(p: P): Ok[String, Option[P]] =
Yes(if (pred(p)) Some(p.repath(p.path)) else None)
}
}
object Ps {
def apply(roost: Path) = new Ps(roost)
} which would translate automatically to something like class Ps(_roost: Path) extends FourFlock[P, Ps](_roost) ...
self =>
lazy val allEs = all map ...
_ collect ...
case e: E => e
lazy val allMs = all map ...
_ collect ...
case m: M => m
protected def justWrap(path: Path) = P(path)
def filtered(pred: P => boolean): Ps = new Ps(roost) with ProxyFlock[P, P, Ps] ...
protected def flocker = self
protected def proxyOf(p: P): Ok[String, Option[P]] = Yes ...
if pred(p) then Some(p.repath(p.path))
else None
object Ps ...
def apply(roost: Path) = new Ps(roost) In particular,
you could
Maybe you just want a temporary val to not pollute your namespace. So
would become
For comprehensions, too:
There may be some places where ellipsis elision makes sense (e.g. after |
In general, I am in favor of doing experiments. And given that Dotty is not Scala 3 yet, it seems like the best place to do these kind of experiments. I think the blog post to be describes it well. That said, I am not sure the comparison to Python is entirely correct. Scala has a lot more powerful tools which will be daunting to beginners regardless the syntax. Python simply doesn't have these and thus it is much easier to learn to whole language. I also think that we should not only look at the potential new developers, but also to the existing developers. In this thread most seem to be happy with the current state, and against white-space sensitive syntax in general. It would be a shame to loose those because they feel they are not being listened to. Even if it turns out that whitespace-sensitive syntax is a clear win, than still I am not sure we should get this in Scala 3, but maybe Scala 4, to not make too many steps at once. Migration tools might help, but more developers will be hesitant since so many things change at the same time. This gives us more time for experimentation, smooth out the syntax, and allows developers to get used to the idea, without endangering the Scala 3 release. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This will crash under -rewrite -noindent if there is no blank line at the end
object testindent:
if 1 > 0 then
println
println
else
println
println
What should be the semantics of start match
case _ => 5
end match
case _ => 5 According to the rules |
@bishabosha In the follow-up PR #7114 this seems to be fixed. |
This PR is now closed in favor of #7114, which implements updated indentation rules. |
I am not entirely sure that we have thought through whether this proposal simplifies things enough. All of these can be used right now to open blocks: for
x <- foo
:
println(x)
This should no longer compile:
But this should:
This is valid (by spec; I haven't tried it):
This should work:
This shouldn't work:
Fine:
Also fine:
Broken:
Fine again:
And this should work:
as should this:
but they do totally different things. And with so many keywords, how do you keep track of what's valid and what's not? Does this work?
Does this?
How about this?
Does this still compile?
Does this work?
What is the content of this list (hint: you'll get warnings)?:
Maybe teaching will reveal that these things aren't problems, or maybe that they are, or maybe it will reveal that they aren't problems when you teach it a particular way, but not reveal what happens when people aren't taught that way. But, anyway, I object strenuously to the notion that this is simple. It can be pretty, but it's complicated and has a fair number of corner cases. People like pretty. And people can learn complicated stuff. But if we're doing it to be simple, it's not; and if we're doing it to simplify, that's not possible because we have to keep al the old stuff and the new syntax interacts with the old in nontrivial ways. |
@Ichoran These are all good points. I opened issue #7136 to continue the discussion. #7114 has now been merged to enable exploration, but that should be the start of a serious discussion, not the end of it. Besides #7136, I also plan to open a thread on contributors to discuss the topic once we have gained more experience with it. |
I won't arg if the no brace syntax is better or worse than the other one. But factually, it is extremelly different and a huge shift from scala 2. Depending if you want to force only one of the two styles for scala 3, I see only huge waste of resources and disapointment coming with that:
All of that is independant from the inherent qualities of each of the syntax. EDIT: typo |
As an experimental feature, allow indentation to be treated as significant.
What is supported?
To get a feel for the code, see the example below. For precise rules, see the doc page.
The old syntax using braces is also supported. Indentation is not significant inside pairs of braces (or other forms of parentheses), so that gives a natural mode without requiring an explicit switch: Once you use a
{
after in a toplevel definition, indentation is off in that code.Why explore this option?
When Scala was first invented, braces ruled. Almost all widely used notations were brace-based. That's why we decided to follow: there were so many other things to innovate, so we explicitly chose to have very conventional syntax.
By now, things are quite different:
So by now indentation is very natural, even obvious, to developers. There's a chance that anything else will increasingly be considered "crufty".
This PR demonstrates that indentation based syntax is quite a nice fit for Scala. So I believe that before finalizing Scala 3 we should seriously consider this syntax as an exploration. We might conclude that indentation has hidden problems, or that its benefits are not enough to balance the cost of change. But to be able to conclude this with confidence we need a serious exploration of this option first.
This PR is intended to enable the exploration. Besides supporting optional significant indentation,
it also provides automatic rewrites from old to new syntax and back. This enables one to migrate
a source quickly and cleanly to indentation based syntax, work with it, and migrate it back
to braces when needed (e.g. before merging it with the master branch). Rewrites maintain formatting and (depending on coding style) it is possible to get back exactly the same source file after going to indentation based and back.
This PR is based on #7024. In fact indentation only works well with new-style control syntax. And rewrites have to be done in two steps. To go from current Scala code to new style indented code one has to invoke the compiler twice, with options
To go the other way, it's also to steps:
While indentation-based syntax requires #7024, the reverse is not true. #7024 is in my mind a definite improvement even if adopted alone. So the two should be considered independently.
Example:
For comparison, here's the current layout of this code, using braces:
A difference between the two styles as shown here is that indentation uses 4 spaces per tab whereas brace-based uses only 2 spaces. This is an arbitrary choice in each case. One could also use 2 spaces for indentation based (Edit: a comment from me below shows the example reworked in this way).