Join GitHub today
GitHub is home to over 28 million developers working together to host and review code, manage projects, and build software together.
Sign upConsider syntax with significant indentation #2491
Comments
odersky
added
the
itype:language enhancement
label
May 20, 2017
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
smarter
May 20, 2017
Member
Audacious :). A few things that come to mind:
- This leave at least one usage where brackets are required: making an anonymous class extending a trait with no additional member
trait A new A {}
- I'm not a fan of the overloaded meaning of
withhere, even in Dotty it's still used to make anonymous classes with multiple parents, how do you interpret the following code?Is thisnew X with Y
new X(Y)ornew X with Y?
|
Audacious :). A few things that come to mind:
|
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
smarter
May 20, 2017
Member
It would also be interesting to compare this proposal with the existing https://github.com/lihaoyi/Scalite
|
It would also be interesting to compare this proposal with the existing https://github.com/lihaoyi/Scalite |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
odersky
May 20, 2017
Contributor
how do you interpret the following code?
new X with
Y
As new X with {Y}. So you should not write code like that. I believe it's really bad code formatting anyway, you should have written
new X
with Y
scalafix will be able to help, I am sure.
As
scalafix will be able to help, I am sure. |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
smarter
May 20, 2017
Member
Taking a step back, what is the with for anyway? What prevents us from being able to write:
xs.map x =>
x + 2
xs.collect
case P1 => E1
case P2 => E2|
Taking a step back, what is the xs.map x =>
x + 2
xs.collect
case P1 => E1
case P2 => E2 |
odersky
referenced this issue
May 20, 2017
Closed
Consider syntax with significant indentation #2488
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
lihaoyi
May 21, 2017
Contributor
Woo!
Some thoughts:
Providing visual cues
To be honest, I think that two-space indents is the main culprit for making the given example hard to read:
def f =
def g =
def h =
def i = 1
i
def j = 2Using 3 or 4-space indents, it's much clearer in most fonts:
def f =
def g =
def h =
def i = 1
i
def j = 2That is the reason all my Scalite examples use 3-space or 4-space indents. Two-space indents works if you have curlies but in most fonts is confusing for indentation-delimited blocks.
I've also experienced working on a large Coffeescript codebase (also indentation-delimited) with 2-space indents, and basic block scoping was definitely confusing and easy to misread compared to our 4-space-indented Python codebase.
w.r.t. Editor support, many editors already support indentation-highlighting to some extent, e.g. IntelliJ's {Ctrl,Cmd}-W "Extend selection " and Sublime Text's Shift-Cmd-J "Expand Selection to Indentation". Going to the start/end of a block is then a matter of highlighting it and then pressing left/right, and presumably it wouldn't be hard to add a shortcut if we wanted it to be a single command.
I think with is too verbose a keyword to use for this purpose
Consider the difference between OCaml style
let foo = bar in
let baz = quxvs Java-style
int foo = bar;
int baz = quxIt's only one character, but I think it makes a huge difference. Python uses : which is close to ideal; in Scala we can't because : is reserved for type ascriptions, but I think it's worth looking for something lighter weight than with
I think functions and classes have a nice symmetry we shouldn't break
def foo(i: Int, s: String = "") = {
def bar() = s * i
println(bar())
}
class Foo(i: Int, s: String = ""){
def bar() = s * i
println(bar())
}
foo(3, "lol")
new Foo(3, "lol")Both take zero-or-more argument lists, both take a block of statements, both run every statement in the block the block. The main "semantic" difference is that functions return the last statement, whereas classes return the entire scope (ignoring "invisible" differences like bytecode representation, initialization-order limits, etc.)
Given the symmetry, If we're going to allow multiline functions with =
def foo(i: Int, s: String) =
def bar() = s * i
println(bar())I think it makes sense to allow multi-line classes with the same syntax
class Foo(i: Int, s: String) =
def bar() = s * i
println(bar())Notably, this is the syntax that F# has chosen https://fsharpforfunandprofit.com/posts/classes/
Given that multi-line while conditionals and for-generators are allowed, what about if-conditionals
Will this work?
if if ({
println("checking...") println("checking...")
var j = i + 1 var j = i + 1
j < 10 j < 10
do }) {
println("small") println("small")
1 1
else } else {
println("big") println("big")
100 100I think it should, for symmetry
I think that we should be able to leave out with in lambdas
xs.map x =>
x + 2
xs.collect
case P1 => E1
case P2 => E2As @smarter mentioned. It might take some hacks (since we don't know it's a lambda until we've already fully tokenized the argument list, and we'll need to go back in time to insert the synthetic curly). Not sure if there's a technical reason it can't be done, but syntactically I think it's unambiguous to a reader, since lambda-argument-lists don't tend to be that long.
|
Woo! Some thoughts: Providing visual cuesTo be honest, I think that two-space indents is the main culprit for making the given example hard to read: def f =
def g =
def h =
def i = 1
i
def j = 2Using 3 or 4-space indents, it's much clearer in most fonts: def f =
def g =
def h =
def i = 1
i
def j = 2That is the reason all my Scalite examples use 3-space or 4-space indents. Two-space indents works if you have curlies but in most fonts is confusing for indentation-delimited blocks. I've also experienced working on a large Coffeescript codebase (also indentation-delimited) with 2-space indents, and basic block scoping was definitely confusing and easy to misread compared to our 4-space-indented Python codebase. w.r.t. Editor support, many editors already support indentation-highlighting to some extent, e.g. IntelliJ's I think
|
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
odersky
May 21, 2017
Contributor
@lihaoyi Thanks for your long and constructive comment. Some recations:
To be honest, I think that two-space indents is the main culprit for making the given example hard to read.
You might well be right. The proposal is silent about how many spaces are to be used. It's a separate discussion, but one which is entangled with the current one.
I am against using = for starting a class body because it's semantically wrong. A class is certainly not the same as its body of declarations. Instead, a class definition introduces a new entity which comes with some declarations.
Multi-line if condition: sure, let's add it.
Leave out with in lambdas: What would the grammar be for this? One of the advantages of the current proposal is that it does not need parser hacks. A smarter lexical analyzer is all that's needed. Also, I think it's important to guard against accidental off-by-one indentations turning into significant nesting.
|
@lihaoyi Thanks for your long and constructive comment. Some recations:
You might well be right. The proposal is silent about how many spaces are to be used. It's a separate discussion, but one which is entangled with the current one. I am against using Multi-line if condition: sure, let's add it. Leave out |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
RichyHBM
May 21, 2017
Just to make sure, IF implemented, would this be an optional flag/command passed to the compiler to indicate this is an indentation file, or would it be more like python where you are forced to use indentation?
Whilst python's indentation based syntax is cool in theory, I have found it to be rather annoying in practice. If you use various different text editors/IDEs that may treat spaces/tabs differently it means you can break your code just by opening it up in a new editor.
You might well be right. The proposal is silent about how many spaces are to be used. It's a separate discussion, but one which is entangled with the current one.
I know this isn't the discussion for the amount of spaces, and I don't intend it to be that. But it is a very real issue that many people are passionate about and I feel it would cause lots of issues. The possible alternative would be to just use tabs and allow the users to set them to the amount they prefer, but then you are probably looking at a space vs tabs discussion..
And just to add to this, Scala is a language that is already criticized as complicated for new comers, I feel adding another restriction would just add to this. Currently you can write your code as you like but having weird code issues based on you not having placed indentation in the right place is just another hurdle.
RichyHBM
commented
May 21, 2017
|
Just to make sure, IF implemented, would this be an optional flag/command passed to the compiler to indicate this is an indentation file, or would it be more like python where you are forced to use indentation? Whilst python's indentation based syntax is cool in theory, I have found it to be rather annoying in practice. If you use various different text editors/IDEs that may treat spaces/tabs differently it means you can break your code just by opening it up in a new editor.
I know this isn't the discussion for the amount of spaces, and I don't intend it to be that. But it is a very real issue that many people are passionate about and I feel it would cause lots of issues. The possible alternative would be to just use tabs and allow the users to set them to the amount they prefer, but then you are probably looking at a space vs tabs discussion.. And just to add to this, Scala is a language that is already criticized as complicated for new comers, I feel adding another restriction would just add to this. Currently you can write your code as you like but having weird code issues based on you not having placed indentation in the right place is just another hurdle. |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
Jasper-M
May 21, 2017
Contributor
I have to agree the indentation based syntax is pretty nice on the eyes in a trivial example. But keeping in mind the cost of change and the fact that indentation based is not strictly better than curly braces based, I think it's probably better not to add it.
|
I have to agree the indentation based syntax is pretty nice on the eyes in a trivial example. But keeping in mind the cost of change and the fact that indentation based is not strictly better than curly braces based, I think it's probably better not to add it. |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
Sciss
May 21, 2017
An exciting proposal. I can follow most arguments. The two things that I don't like is 'lambdas with with' and 'interpreted end-comments'. The first one frankly just looks awkward, so I would be with @lihaoyi here to not use the with keyword in this case. Also, the number of alternative ways of writing a map just explodes:
xs.map(x => ...)
xs.map { x => ... }
xs map { x => ... }
xs.map with x => ...
xs map with x => ... // ?
When I look through the examples, the lambda ones are the ones that stand out as outlandish to me.
The interpreted-end-comments I find very problematic. You are instructing the compiler now to make sense of line comments. I also think this will make life miserable for parser, editor and IDE authors. People can still use curly braces, no? Then we can stick to braces if the block has many lines that make it difficult to see the end. As I understand, there will be already at least a period where both curly braces and indentation are allowed. My guess is, people will use indentation for short (< 10 lines) blocks, and keep braces for longer blocks. Perhaps this mix is actually a good solution in terms of readability. If not, why not use a real keyword end, such as
def f =
def g =
...
(long code sequence)
...
end f
def h
?
As several people have said, using indentation almost certainly means we will have to use at least three spaces to come out clearly and visibly, Perhaps this setting could be a scalacOption with an optional (linter) switch to emit warnings if the code uses other indentation size.
Overall, I think this is great and I hope it will be implemented. People (including myself, I think) have argued against this when Python came along, but people have also gotten used to it, and I think the success of Python and the cleanness of Ocaml/F# clearly speak in favour of reducing the visual clutter.
I'm looking forward to having if then constructs and the possibility to have multi-line conditions in if and
while without the awkward ({ ... }).
Sciss
commented
May 21, 2017
•
|
An exciting proposal. I can follow most arguments. The two things that I don't like is 'lambdas with
When I look through the examples, the lambda ones are the ones that stand out as outlandish to me. The interpreted-end-comments I find very problematic. You are instructing the compiler now to make sense of line comments. I also think this will make life miserable for parser, editor and IDE authors. People can still use curly braces, no? Then we can stick to braces if the block has many lines that make it difficult to see the end. As I understand, there will be already at least a period where both curly braces and indentation are allowed. My guess is, people will use indentation for short (< 10 lines) blocks, and keep braces for longer blocks. Perhaps this mix is actually a good solution in terms of readability. If not, why not use a real keyword
? As several people have said, using indentation almost certainly means we will have to use at least three spaces to come out clearly and visibly, Perhaps this setting could be a Overall, I think this is great and I hope it will be implemented. People (including myself, I think) have argued against this when Python came along, but people have also gotten used to it, and I think the success of Python and the cleanness of Ocaml/F# clearly speak in favour of reducing the visual clutter. I'm looking forward to having |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
lihaoyi
May 21, 2017
Contributor
Three more notes:
I agree that // end comments look terrible
If we want them to be a part of the syntax, we should introduce syntax for them. Or we could let people use curly-brackets optionally.
I think both cases aren't necessary; in Python people get by just fine without end delimiters, but anything is better than // end comments!
How optional will this syntax be?
Will it be a per-project setting? Per-file? Perhaps even more fine-grained, where you can mix in curlies and indentation-based syntax?
Can we get rid of do-while loops?
This
do
println("x")
println("y")
while
println("z")
true
Is isormorphic to the while-loop
while
println("x")
println("y")
println("z")
true
do
()In fact, the while-loop version is superior, because the while block in a do-while does not have access to the things in the while block, whereas in the while-do-() case the conditional has everything defined in the while block in scope. This has proven extremely useful to me on many occasions, and given that this new syntax (potentially) makes while-do-() as nice to use as do-while, I think we can really just drop the do-while syntax case entirely
|
Three more notes: I agree that
|
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
Sciss
May 21, 2017
while println("x") println("y") println("z") true do ()
Sorry, but that is horrible. That reminds me of C code where everything is written in the for statement, like for {do some stuff) ;;
Sciss
commented
May 21, 2017
•
Sorry, but that is horrible. That reminds me of C code where everything is written in the |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
lihaoyi
May 21, 2017
Contributor
We could let the syntax leave out the do
while
println("x")
println("y")
println("z")
trueNot so bad? At least I think it's not too bad...
|
We could let the syntax leave out the while
println("x")
println("y")
println("z")
trueNot so bad? At least I think it's not too bad... |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
bmjsmith
May 21, 2017
This offers no objective improvement to the language at a cost that is not insignificant. More overloaded keywords is the last thing that helps newbies and supporting two styles or switching between them is burdensome. Developers in general will not reach a consensus on indentation vs delimiters any more than they will on tabs vs spaces or which line your curly brackets go on. Please don't facilitate wasting effort debating this (or having to switch) in every project and leave it as it is.
bmjsmith
commented
May 21, 2017
|
This offers no objective improvement to the language at a cost that is not insignificant. More overloaded keywords is the last thing that helps newbies and supporting two styles or switching between them is burdensome. Developers in general will not reach a consensus on indentation vs delimiters any more than they will on tabs vs spaces or which line your curly brackets go on. Please don't facilitate wasting effort debating this (or having to switch) in every project and leave it as it is. |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
Jasper-M
May 21, 2017
Contributor
Also, what about the "scalable language" thing, where you can implement your own language constructs?
def until(pred: =>Boolean)(body: =>Unit) = while (!pred) body
And then
var i = 0
until (i == 10) {
i += 1
println(i)
}
Does that work as seamless with indentation as well?
Edit: to answer my own question: not in the current proposal. You would have
var i = 0
until (i == 10) with
i += 1
println(i)
|
Also, what about the "scalable language" thing, where you can implement your own language constructs?
And then
Does that work as seamless with indentation as well? Edit: to answer my own question: not in the current proposal. You would have
|
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
optician
May 21, 2017
About motivation:
Cleaner typography
With whitespace approach you'll definitely use at least 4-space indent that cuts off available horizontal space. Line breaks would appear more often.
Regain control of vertical white space.
Please could you provide example?
Ease of learning. There are some powerful arguments why indentation based syntax is easier to learn.
Opinion based. My experience with novices and all histories I heard don't have evidences of such problem.
Less prone to errors.
Indentation error is new syntax error for language. So it difficult to say it less prone to errors. Working in REPL also would be harder.
Easier to change.
Agree. But imho one case is not big deal.
Better vertical alignment.
Agree. I use the same style. So problem is absent.
optician
commented
May 21, 2017
•
|
About motivation:
With whitespace approach you'll definitely use at least 4-space indent that cuts off available horizontal space. Line breaks would appear more often.
Please could you provide example?
Opinion based. My experience with novices and all histories I heard don't have evidences of such problem.
Indentation error is new syntax error for language. So it difficult to say it less prone to errors. Working in REPL also would be harder.
Agree. But imho one case is not big deal.
Agree. I use the same style. So problem is absent. |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
shawjef3
May 21, 2017
There is another impediment: Replacing braces with whitespace breaks the principle of don't-repeat-yourself, which I believe most code authors believe in. Braces are a factoring on lines of code to say they are in a particular scope. Removing braces means that each line defines what scope it is in.
shawjef3
commented
May 21, 2017
|
There is another impediment: Replacing braces with whitespace breaks the principle of don't-repeat-yourself, which I believe most code authors believe in. Braces are a factoring on lines of code to say they are in a particular scope. Removing braces means that each line defines what scope it is in. |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
polkovnikov-ph
May 21, 2017
Great news.
If certain keywords are followed
Could it be simplified to a much simpler rule: enclose every extra level of tabulation with a pair of curly braces? I've implemented significant whitespace this way in my own similar language, and had no issues with context at all.
Susceptibility to off-by-one indentation
That's easily solved by also enforcing tabs at the beginning of the line. As a side effect, there would be no more need to have an exact count of spaces per tab in style guides.
polkovnikov-ph
commented
May 21, 2017
|
Great news.
Could it be simplified to a much simpler rule: enclose every extra level of tabulation with a pair of curly braces? I've implemented significant whitespace this way in my own similar language, and had no issues with context at all.
That's easily solved by also enforcing tabs at the beginning of the line. As a side effect, there would be no more need to have an exact count of spaces per tab in style guides. |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
odersky
May 21, 2017
Contributor
Wow, this proposal has generated a lot of heat (should have expected that!) I think for now my proposed strategy will be:
-
have this or a variant of it as an optional feature in early versions of Dotty, controlled by a command-line flag.
-
get automatic reformatters that can switch between braces and indentation.
-
experiment with the feature and get feedback from users.
Once the experiments are in, decide on whether we want to keep this.
|
Wow, this proposal has generated a lot of heat (should have expected that!) I think for now my proposed strategy will be:
Once the experiments are in, decide on whether we want to keep this. |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
klass-ivan
May 21, 2017
I like the idea of indent syntax. However, in a big project it can be hard to change all the codebase at once, so I think this compiler option should be controlled per-file. Also we can think of more powerful syntax migration mechanism or convention, for example, like "from future import " in Python.
As for "with" for lamdas, it personally looks kinda awkward to me, but acceptable.
klass-ivan
commented
May 21, 2017
|
I like the idea of indent syntax. However, in a big project it can be hard to change all the codebase at once, so I think this compiler option should be controlled per-file. Also we can think of more powerful syntax migration mechanism or convention, for example, like "from future import " in Python. |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
Blaisorblade
May 21, 2017
Contributor
[...] That rule proves to be quite constraining (for instance it would outlaw the chained filter and map operations in the example below), so it is currently not implemented.
I'm afraid of too much flexibility, based on my experience with layout-sensitive syntax in Haskell.
I can't fully pinpoint the problem there, and I'm sorry this isn't too constructive yet, but I know that indenting Haskell code, and offering editor support for it, is a rather nontrivial matter (I've used some Emacs indentation modes, and I've never been fully happy with any of those—see http://haskell.github.io/haskell-mode/manual/13.16/Indentation.html for a list).
The topic is so tricky that one of these indentation modes deserved a 10-page JFP paper (which I haven't studied), "Dynamic tabbing for automatic indentation with the layout rule", http://www.cs.tufts.edu/~nr/cs257/archive/guy-lapalme/layout.pdf.
TL;DR. I'm happy if we don't drown in flexibility. I could be fine with following a style guide—as long as the rules allow for satisfactory styles.
EDIT: please tag me if you want to reply to me and have me read your answer—gotta unsubscribe from this busy issue.
I'm afraid of too much flexibility, based on my experience with layout-sensitive syntax in Haskell. I can't fully pinpoint the problem there, and I'm sorry this isn't too constructive yet, but I know that indenting Haskell code, and offering editor support for it, is a rather nontrivial matter (I've used some Emacs indentation modes, and I've never been fully happy with any of those—see http://haskell.github.io/haskell-mode/manual/13.16/Indentation.html for a list). The topic is so tricky that one of these indentation modes deserved a 10-page JFP paper (which I haven't studied), "Dynamic tabbing for automatic indentation with the layout rule", http://www.cs.tufts.edu/~nr/cs257/archive/guy-lapalme/layout.pdf. TL;DR. I'm happy if we don't drown in flexibility. I could be fine with following a style guide—as long as the rules allow for satisfactory styles. EDIT: please tag me if you want to reply to me and have me read your answer—gotta unsubscribe from this busy issue. |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
rabbitonweb
commented
May 21, 2017
|
OH "While we at it, can we add dynamic typing as well?" :) |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
ghost
May 21, 2017
In this same mindset, we should go one step further and remove lane delimiters from streets. Also stop, yield signs, traffic lights, and create implicit rules that people can follow in their heads. That would make streets look more elegant.
ghost
commented
May 21, 2017
•
|
In this same mindset, we should go one step further and remove lane delimiters from streets. Also stop, yield signs, traffic lights, and create implicit rules that people can follow in their heads. That would make streets look more elegant. |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
rabbitonweb
commented
May 21, 2017
|
That was a joke, lol. |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
obask
May 22, 2017
I feel like forcing number of indents is a terrible idea: Haskell and LISP use num of spaces equal to previous line keyword, like:
(do-some-stuff people
_______________user)
Instead of:
(do-some-stuff people
__user)
obask
commented
May 22, 2017
|
I feel like forcing number of indents is a terrible idea: Haskell and LISP use num of spaces equal to previous line keyword, like: |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
jpallas
May 22, 2017
I think @lihaoyi's point above about with and : cannot be overemphasized. Python's syntax works in large part because : (at end of line) reliably signals a new block and does so in a visually distinctive way that a keyword cannot. But Python doesn't have the challenge of blocks within an expression. There might simply be no good way to do this for Scala.
jpallas
commented
May 22, 2017
|
I think @lihaoyi's point above about |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
lihaoyi
May 22, 2017
Contributor
Wow, this proposal has generated a lot of heat (should have expected that!)
Surely this is expected. The response is actually milder than I'd expect; imagine submitting a curly-brace PIP to the Python community!
Surely this is expected. The response is actually milder than I'd expect; imagine submitting a curly-brace PIP to the Python community! |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
jpallas
May 22, 2017
imagine submitting a curly-brace PIP to the Python community
>>> from __future__ import braces
File "<stdin>", line 1
SyntaxError: not a chance
jpallas
commented
May 22, 2017
|
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
kmizu
May 22, 2017
Certainly, the new syntax proposal is great. At the same time, the current style is not so problematic in real Scala world. Such a big change should not be introduced to solve a little problems in my opinions.
kmizu
commented
May 22, 2017
•
|
Certainly, the new syntax proposal is great. At the same time, the current style is not so problematic in real Scala world. Such a big change should not be introduced to solve a little problems in my opinions. |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
pkolaczk
May 22, 2017
Generally I like this proposal. Braces offer actually too much flexibility on how things can be formatted and make a lot of visual noise.
Indent based nesting is more natural, because it is also the way how we structure content in written natural language.
What I don't like about this proposal:
- Overloaded meaning of 'with'.
- Meaningful comments for end of the block; why not make it a part of the syntax and have an optional 'end' keyword (not comment) like in Basic? However, I like the concept of marking end of block this way rather than a brace. It is much more readable.
Also +1 for an automated formatter.
pkolaczk
commented
May 22, 2017
|
Generally I like this proposal. Braces offer actually too much flexibility on how things can be formatted and make a lot of visual noise. Indent based nesting is more natural, because it is also the way how we structure content in written natural language. What I don't like about this proposal:
Also +1 for an automated formatter. |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
lihaoyi
May 22, 2017
Contributor
Thinking about this a bit more, I think if we're going to have to overload a keyword to annotate significant indentation, we should overload : rather than with. Thus, any trailing : on a line will (backwards-incompatibly) be taken to mean the opening of a new indentation-delimited block, rather than a type ascription.
: it's something that many people will already be familiar with from Python, is concise, distinct (not just another english keyword in a sea of english keywords) and is relatively neutral with regard to what it "means": it just means a new block-scope, without any english-connotations to worry about.
Furthermore, I think lines with trailing withs are probably more common in existing Scala code than lines with trailing :, though it's an empirical question whether that's through throughout the community.
It's not great to overload keywords, but if we're going to be overloading something anyway I think overloading : makes a lot more sense than overloading with
package p:
object o:
class C extends Object
with Serializable:
val x = new C:
def y = 3
val result =
if (x == x):
println("yes")
true
else
println("no")
false for
x <- List(1, 2, 3)
y <- List(x + 1)
yield
x + y
for
x <- List(1, 2, 3)
y <- List(x + 1)
do
println(x + y)
// Try expressions
try
val x = 3
1.0 / x
catch
case ex: Exception =>
0
finally
println("done")
// Match expressions@odersky what do you think?
EDIT: I still think it would be best to be able to have no delimiter at all after the last token of a class/object/package header. Whether it's technically easy to do or not I don't really know, but I think it's definitely worth discussing on a "would that be a nice syntax to have" even if it turns out to be hard to implement
package p
object o
class C extends Object
with Serializable
val x = new C
def y = 3
val result =
if (x == x):
println("yes")
true
else
println("no")
false|
Thinking about this a bit more, I think if we're going to have to overload a keyword to annotate significant indentation, we should overload
Furthermore, I think lines with trailing It's not great to overload keywords, but if we're going to be overloading something anyway I think overloading package p:
object o:
class C extends Object
with Serializable:
val x = new C:
def y = 3
val result =
if (x == x):
println("yes")
true
else
println("no")
false for
x <- List(1, 2, 3)
y <- List(x + 1)
yield
x + y
for
x <- List(1, 2, 3)
y <- List(x + 1)
do
println(x + y)
// Try expressions
try
val x = 3
1.0 / x
catch
case ex: Exception =>
0
finally
println("done")
// Match expressions@odersky what do you think? EDIT: I still think it would be best to be able to have no delimiter at all after the last token of a class/object/package header. Whether it's technically easy to do or not I don't really know, but I think it's definitely worth discussing on a "would that be a nice syntax to have" even if it turns out to be hard to implement package p
object o
class C extends Object
with Serializable
val x = new C
def y = 3
val result =
if (x == x):
println("yes")
true
else
println("no")
false |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
pkolaczk
May 22, 2017
Will I be able to write short expressions in a single line?
This looks weird:
val x = if y: 0 else 1
Honestly, I prefer:
val x = if y then 0 else 1
A nice thing about keywords like if .. then .. else or while .. do is that they allow to drop parens, even if the expression is formatted in a single line.
pkolaczk
commented
May 22, 2017
•
|
Will I be able to write short expressions in a single line? This looks weird: Honestly, I prefer: A nice thing about keywords like |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
pkolaczk
May 22, 2017
Actually:
val x = if y then 0 else 1
looks to me even better than the current:
val x = if (y) 0 else 1
Here we have a mixture of syntax based on punctuation (parens) and keyword (else).
pkolaczk
commented
May 22, 2017
|
Actually:
looks to me even better than the current:
Here we have a mixture of syntax based on punctuation (parens) and keyword (else). |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
Sciss
May 22, 2017
After looking at Martin's original proposal again, and comparing with the colon proposal by Lihaoyi, I think it makes more sense to stick to the original idea. It looks good for all cases where with is not required: for, while, if then, try, match, def name =. So the problem IMO is only the lambdas and the class definitions.
Sciss
commented
May 22, 2017
•
|
After looking at Martin's original proposal again, and comparing with the colon proposal by Lihaoyi, I think it makes more sense to stick to the original idea. It looks good for all cases where |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
pkolaczk
May 22, 2017
Is there really any good reason we need with in the original proposal?
Why is this a problem:
class C1
class C2
def inc(x: Int): Int = // this is indented, so the compiler would know we continue C2
x + 1
end C2 // optional
pkolaczk
commented
May 22, 2017
•
|
Is there really any good reason we need Why is this a problem:
|
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
jvvasques
May 22, 2017
This is a really interesting topic and I'm very happy to see it debated with so many constructive arguments. I wanted to give my opinion as well.
I believe that removing { makes code more elegant and pleasant to read. However, I'm not the biggest fan of the python indentation approach. One of the few things I like about Ruby is the elegant and concise syntax. I think the Ruby guys and now Elixir by the hands of Jose Valim made some really interesting choices there.
May I suggest something like this.
Blocks
def f(x: Int) =
val y = x * x
y + 1
endmatch expressions:
xs match do
case x :: xs1 => ...
case Nil => ...
enddelimit statement sequences in objects and classes. Example:
object Obj
class C(x: Int)
def f = x + 3
end
def apply(x: Int) = new C(x)
endPassing arguments that were formerly in braces to functions. Examples:
xs.map do x =>
x + 2
end
xs.collect do
case P1 => E1
case P2 => E2
endWould love to get some feedback on this
jvvasques
commented
May 22, 2017
•
|
This is a really interesting topic and I'm very happy to see it debated with so many constructive arguments. I wanted to give my opinion as well. I believe that removing May I suggest something like this. Blocks def f(x: Int) =
val y = x * x
y + 1
endmatch expressions: xs match do
case x :: xs1 => ...
case Nil => ...
enddelimit statement sequences in objects and classes. Example: object Obj
class C(x: Int)
def f = x + 3
end
def apply(x: Int) = new C(x)
endPassing arguments that were formerly in braces to functions. Examples: xs.map do x =>
x + 2
end
xs.collect do
case P1 => E1
case P2 => E2
endWould love to get some feedback on this |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
danielyli
May 22, 2017
To assess the risks and benefits of an indentation-based language, it's helpful to look to the accumulated knowledge from real-world experiences of using them in a large organization. Probably very few companies have as large a Python codebase as Google.
The creators of Go have this to say about why their experience with Python at Google informed them that indentation-based code blocks were not as safe or dependable as C-style braces:
Some observers objected to Go's C-like block structure with braces, preferring the use of spaces for indentation, in the style of Python or Haskell. However, we have had extensive experience tracking down build and test failures caused by cross-language builds where a Python snippet embedded in another language, for instance through a SWIG invocation, is subtly and invisibly broken by a change in the indentation of the surrounding code. Our position is therefore that, although spaces for indentation is nice for small programs, it doesn't scale well, and the bigger and more heterogeneous the code base, the more trouble it can cause. It is better to forgo convenience for safety and dependability, so Go has brace-bounded blocks.
-- Rob Pike, Google
danielyli
commented
May 22, 2017
|
To assess the risks and benefits of an indentation-based language, it's helpful to look to the accumulated knowledge from real-world experiences of using them in a large organization. Probably very few companies have as large a Python codebase as Google. The creators of Go have this to say about why their experience with Python at Google informed them that indentation-based code blocks were not as safe or dependable as C-style braces:
|
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
vanDonselaar
May 22, 2017
I appreciate the open attitude towards change and improvement of the language. However, I think significant whitespacing is a mistake. A few practical things that I've experienced while using the language that inspired this proposal (Python).
-
Cutting/copying code blocks won't work properly. You have to fix the indentation yourself. A good IDE could help, but I can hardly imagine it will be as easy as it is now. The semantics of the pasted code will depend on the position of the cursor rather than the position in the AST.
-
More error prone. I've seen automatic git 3-way merges where the indent of the final merge was incorrect. In python that was something like:
for [...]
doSomething() """ <-- oops """The counter argument that this mistake can happen:
if (condition)
println("something")
action()is not solid very solid and is ironically caused by the lack of curly's I'm advocating. Curly's for if statements spanning multiple lines can easily be enforced with a linter. Also: the IntelliJ auto-formatter removes the two spaces before action(), so this bug is easy to detect.
-
The diffs become noisy because you can't practically justify the use of 'Ignore whitspaces and empty lines'. So if you remove one
ifstatement around a code block ofXlines, your diff will containX + 1lines instead of2lines (assuming Egyptian style curly's). -
Auto code formatting will become even more complex. I'm not an expert on code formatting, but after reading http://journal.stuffwithstuff.com/2015/09/08/the-hardest-program-ive-ever-written/ by Bob Nystrom, I have the idea that we should try to make this problem easier rather than harder. ;)
Apart from the practical problems, I still don't see what problem is solved here. I'm afraid it's only a matter of aesthetics.
vanDonselaar
commented
May 22, 2017
•
|
I appreciate the open attitude towards change and improvement of the language. However, I think significant whitespacing is a mistake. A few practical things that I've experienced while using the language that inspired this proposal (Python).
for [...]
doSomething() """ <-- oops """The counter argument that this mistake can happen: if (condition)
println("something")
action()is not solid very solid and is ironically caused by the lack of curly's I'm advocating. Curly's for
Apart from the practical problems, I still don't see what problem is solved here. I'm afraid it's only a matter of aesthetics. |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
olafurpg
May 22, 2017
Contributor
I made an experiment to see how this change would impact code in the wild.
I implemented a naive scalafix rewrite to remove all curly braces as long as the open/close pair is not on the same line: scalacenter/scalafix@a921c42
This rewrite does not handle the with syntax or validate that the indentation inside blocks/templates is consistently 2 spaces. This rewrite is only meant to give a rough feel for how the diff looks like.
I ran the rewrite on ~30k source files (~3 million lines of code) from the projects: PredictionIO akka breeze cats ensime-server fastparse finagle framework gitbucket goose intellij-scala kafka lila marathon pickling platform playframework saddle sbt scala scala-js scalafx scalafx-ensemble scalatra scalaz scalding scaloid shapeless slick spark spire summingbird util.
The diff from running the rewrite is here: olafurpg/scala-repos#21
Some observations:
- without curly braces, how do we distinguish between a term application with block arguments (like
foo {\n blah\n}) and infix operators where the operator follows a line break
(foo
andThen bar
andThen baz)
// term application with block argument
(foo {
println(1)
qux
})
// without curly, is this supposed to work or not?
(foo
println (1)
qux
)- vertical alignment is commonly used in Scala, and I believe many people use the
-wflag or&w=1url parameter in Github during code review to ignore whitespace changes caused by vertical alignment. By making indent significant, you may need to think twice before using-wor&w=1. - Deeply nested code becomes hard to follow in my opinion, 4 space indent may help. Example Spray routes https://github.com/olafurpg/scala-repos/blob/1225fa10eb67934d7c15fec745ceefe43005533b/repos/PredictionIO/data/src/main/scala/io/prediction/data/api/EventServer.scala#L334-L385
|
I made an experiment to see how this change would impact code in the wild. I implemented a naive scalafix rewrite to remove all curly braces as long as the open/close pair is not on the same line: scalacenter/scalafix@a921c42 I ran the rewrite on ~30k source files (~3 million lines of code) from the projects: PredictionIO akka breeze cats ensime-server fastparse finagle framework gitbucket goose intellij-scala kafka lila marathon pickling platform playframework saddle sbt scala scala-js scalafx scalafx-ensemble scalatra scalaz scalding scaloid shapeless slick spark spire summingbird util. The diff from running the rewrite is here: olafurpg/scala-repos#21 Some observations:
(foo
andThen bar
andThen baz)
// term application with block argument
(foo {
println(1)
qux
})
// without curly, is this supposed to work or not?
(foo
println (1)
qux
)
|
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
nafg
commented
May 22, 2017
|
@RichyHBM how is that better? |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
RichyHBM
May 22, 2017
@nafg It was more of an idea to try and please both worlds, keeping scala as is but also providing a mechanism for developers to use these slightly more controversial features. Of course the compiler/back-end would need to support all these features equally but it could allow people developing in the language to know that a .scala file will always conform to the current scala language.
Whilst a feature is an addition to the language, this feels more like a change to what is already in the language, and as such I feel it deserves some differentiation
RichyHBM
commented
May 22, 2017
|
@nafg It was more of an idea to try and please both worlds, keeping scala as is but also providing a mechanism for developers to use these slightly more controversial features. Of course the compiler/back-end would need to support all these features equally but it could allow people developing in the language to know that a .scala file will always conform to the current scala language. Whilst a feature is an addition to the language, this feels more like a change to what is already in the language, and as such I feel it deserves some differentiation |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
keithasaurus
May 22, 2017
Aside from history, I've never heard a good reason for curly braces to exist in this day-and-age. They reduce the signal-to-noise ratio of code. Significant spacing (and strict style requirements in general) lead to more focus on the logic and less focus on the format.
From personal experience, I'd also suggest not including an "end" statement. Hasn't been necessary in any of the code I've written in Python, elm, haskell, or coffeescript,
I agree with a lot of people that allowing two syntaxes is not going to be sustainable for the language. Would it be a great challenge to write an automated converter and deprecate curly braces?
keithasaurus
commented
May 22, 2017
|
Aside from history, I've never heard a good reason for curly braces to exist in this day-and-age. They reduce the signal-to-noise ratio of code. Significant spacing (and strict style requirements in general) lead to more focus on the logic and less focus on the format. From personal experience, I'd also suggest not including an "end" statement. Hasn't been necessary in any of the code I've written in Python, elm, haskell, or coffeescript, I agree with a lot of people that allowing two syntaxes is not going to be sustainable for the language. Would it be a great challenge to write an automated converter and deprecate curly braces? |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
DarkDimius
May 22, 2017
Member
I've never heard a good reason for curly braces to exist in this day-and-age.
While it is not clear if experience is transferable between languages, but Go, a recently designed language of our day-and-age, made a conscious decision to not use significant spacing. See above: #2491 (comment).
While it is not clear if experience is transferable between languages, but Go, a recently designed language of our day-and-age, made a conscious decision to not use significant spacing. See above: #2491 (comment). |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
obask
May 22, 2017
Have you considered using Haskell's "application operator" instead of "with" keyword?
For me both ways are readable:
take 1 $ filter even [1..10]
-- OR
take 1 with filter even [1..10]https://stackoverflow.com/a/19521376/1205211
PS I'm also not a big fan of "val"s on every line, even golang doesn't have this rudiment.
obask
commented
May 22, 2017
|
Have you considered using Haskell's "application operator" instead of "with" keyword? take 1 $ filter even [1..10]
-- OR
take 1 with filter even [1..10]https://stackoverflow.com/a/19521376/1205211 |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
keithasaurus
May 22, 2017
Go is a very specific language, though. And it's written very specifically for google's own preferences. I mean I'm not sure the way it does garbage collection, generics, automated/enforced formatting (I do like this!) or other things is applicable to every language/situation. They may have run into issues with spacing every once in a while causing issues, but curly braces is an inefficiency in every part of your code. More characters, more lines, less homogeneous code.
For me, the lesson to learn from Python is that readability counts. Because aside from readability, there are very few things that Python does well. And yet it's one of the most widely used languages today.
keithasaurus
commented
May 22, 2017
•
|
Go is a very specific language, though. And it's written very specifically for google's own preferences. I mean I'm not sure the way it does garbage collection, generics, automated/enforced formatting (I do like this!) or other things is applicable to every language/situation. They may have run into issues with spacing every once in a while causing issues, but curly braces is an inefficiency in every part of your code. More characters, more lines, less homogeneous code. For me, the lesson to learn from Python is that readability counts. Because aside from readability, there are very few things that Python does well. And yet it's one of the most widely used languages today. |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
shawjef3
May 22, 2017
Readability is important, but so are write-ability and edit-ability. Braces give us the second two while taking away from the first in only the smallest way (trailing '}').
shawjef3
commented
May 22, 2017
|
Readability is important, but so are write-ability and edit-ability. Braces give us the second two while taking away from the first in only the smallest way (trailing '}'). |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
shawjef3
May 22, 2017
Does the while block in the following go with the first or second do block?
do
println("x")
println("y")
while
println("z")
true
do
println("x")
println("y")
shawjef3
commented
May 22, 2017
|
Does the do
println("x")
println("y")
while
println("z")
true
do
println("x")
println("y") |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
keithasaurus
May 22, 2017
@shawjef3
I agree that the do/while above is weird. Although, in your example, it would likely be that the first do while is valid and then the second would look like an incomplete do while
I think the implementation should look like:
do
println("x")
println("y")
while (true)
// this code would be invalid.
println("z")
true
do
println("x")
println("y")
Or without parens:
do
println("x")
println("y")
while true // this wouldn't be able to appear on the next line
This would also look weird with curly braces:
do {
....
} while {
....
} do {
....
}
I'm not sure I've seen a language where both do and while can have numerous statements like in the above, and I'm not sure I understand the point.
Either way, that's implementation-specific. I don't think it has anything to do with space significance in general.
keithasaurus
commented
May 22, 2017
|
@shawjef3 I think the implementation should look like:
Or without parens:
This would also look weird with curly braces:
I'm not sure I've seen a language where both Either way, that's implementation-specific. I don't think it has anything to do with space significance in general. |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
nafg
May 23, 2017
@keithasaurus some have been given in this thread. Can you explain why you don't think they are issues?
nafg
commented
May 23, 2017
|
@keithasaurus some have been given in this thread. Can you explain why you don't think they are issues? |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
shawjef3
May 23, 2017
@keithasaurus My thinking is that a do block without a 'while` would be valid, because such a block in existing code is valid. Example:
class X {
{
println("valid")
}
}
shawjef3
commented
May 23, 2017
|
@keithasaurus My thinking is that a class X {
{
println("valid")
}
} |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
floating-cat
May 23, 2017
Looks no one mention Sass (a stylesheet language). Sass consists of two syntaxes, you can use the indented syntax if you use .sass file extension, brackets style if you use .scss file extension. Sass 3 introduced the latter one which is fully compatible with the syntax of CSS. Because Sass is much simpler to Scala, Sass two visually distinct ways don't have the similar problems mention above like Scala, but maybe we can still learn something from Sass. Though I am not sure whether use two different file extension is a good idea for Scala or we can use both style in the same file.
If you use use IntelliJ IDEA and copy-paste Java code into Kotlin file, and IDE will suggest to convert it. I think we can follow the same way if we adopt this proposal, the IDE or code editor could suggest to convert it to the indented/brackets style if you copy code from StackOverflow or other places. Though the pitfall still exists if you paste you code in the incorrect indented place.
floating-cat
commented
May 23, 2017
•
|
Looks no one mention Sass (a stylesheet language). Sass consists of two syntaxes, you can use the indented syntax if you use If you use use IntelliJ IDEA and copy-paste Java code into Kotlin file, and IDE will suggest to convert it. I think we can follow the same way if we adopt this proposal, the IDE or code editor could suggest to convert it to the indented/brackets style if you copy code from StackOverflow or other places. Though the pitfall still exists if you paste you code in the incorrect indented place. |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
som-snytt
May 23, 2017
Why did Martin use braces in the first place? To keep his pants up, of course.
This is an especially fun thread. I'm grateful to listen in on a vigorous and invigorating debate about a language getting long in the tooth.
Overloading : seems OK, as it is bound to pass _ on StackOverflow for "What are all the uses of colon in Scala?" Besides sequence wildcard, the meaning of trailing colon for operator associativity means you must remember to insert a space because colon is not punctuation. def *: Double.
White space also matters for nl inference. I forget the precise rules, so I take a refresher course on Coursera every year, the one called, nl? np!
So really we have been primed for significant white space. I don't even know why we must still call it white space, as it depends on your color scheme. White space supremacy ought to end with dotty.
Since a space is just an underscore without a score, one might honor a certain project by calling it the underscoreless, and we are concerned here with significant underscorelessness.
som-snytt
commented
May 23, 2017
|
Why did Martin use braces in the first place? To keep his pants up, of course. This is an especially fun thread. I'm grateful to listen in on a vigorous and invigorating debate about a language getting long in the tooth. Overloading White space also matters for So really we have been primed for significant white space. I don't even know why we must still call it Since a space is just an underscore without a score, one might honor a certain project by calling it the |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
godenji
May 23, 2017
Can adapt to either syntax, though staying the course with existing brace based syntax is the path of least resistance (i.e. do nothing). Braces/indentation aside, it would be wonderful if Dotty somehow "fixed" the following syntactic requirements:
if(cond) x else y
if cond then x else y
foo match {
case x: Bar =>
case x: Baz =>
}
foo match {
x: Bar =>
x: Baz =>
}
If the case keyword were relegated to case class and case object definitions that would be wonderful. Apparently implicit functions will allow the removal of thousands of implicit method annotations from the Dotty compiler; eliminating case from pattern matching blocks would remove perhaps millions of case entries from current Scala code bases in the world.
godenji
commented
May 23, 2017
|
Can adapt to either syntax, though staying the course with existing brace based syntax is the path of least resistance (i.e. do nothing). Braces/indentation aside, it would be wonderful if Dotty somehow "fixed" the following syntactic requirements:
If the |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
AlexGalays
May 23, 2017
It has novelty value but this has got to have the lowest priority ever
Never heard of anyone struggling with braces; heard about plenty of people struggling with other things.
AlexGalays
commented
May 23, 2017
|
It has novelty value but this has got to have the lowest priority ever Never heard of anyone struggling with braces; heard about plenty of people struggling with other things. |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
V-Lamp
May 23, 2017
If both syntaxes are available at once, then it becomes one more choice on the developer's head: "Should I use braces or not for this expression?". A well iterated point on improving productivity is to minimize the amount of daily choices one makes. Scala has always been on the "more than one way" side, but I feel this takes it to a needless extreme.
If only one syntax is allowed, then there has to be a compiler flag. Within an organisation you can enforce a uniform coding style (and set of flags), but not across the internet. Imagine the horror on StackOverflow: "You need to set this compiler flag to X to run this snippet". What about copy-pasting "legacy" code snippets from SO into a brace-less dotty codebase?
From a language designer perspective, this is a minor parser change, but from a user perspective it is like a new language. As AlexGalays says above, the value of this compared to its risk/cost feels tiny.
As with every major language version, the risk of split on major versions is very real (Python...). It would be great for Scala to focus instead on minimizing that overhead across use cases and even after the first wave of adoption (and I know there is active work on this with Scala meta but this does not cover all use cases) and ensure a smooth and rather uniform transition. A thriving Scala is in our common interest
V-Lamp
commented
May 23, 2017
|
If both syntaxes are available at once, then it becomes one more choice on the developer's head: "Should I use braces or not for this expression?". A well iterated point on improving productivity is to minimize the amount of daily choices one makes. Scala has always been on the "more than one way" side, but I feel this takes it to a needless extreme. If only one syntax is allowed, then there has to be a compiler flag. Within an organisation you can enforce a uniform coding style (and set of flags), but not across the internet. Imagine the horror on StackOverflow: "You need to set this compiler flag to X to run this snippet". What about copy-pasting "legacy" code snippets from SO into a brace-less dotty codebase? From a language designer perspective, this is a minor parser change, but from a user perspective it is like a new language. As AlexGalays says above, the value of this compared to its risk/cost feels tiny. As with every major language version, the risk of split on major versions is very real (Python...). It would be great for Scala to focus instead on minimizing that overhead across use cases and even after the first wave of adoption (and I know there is active work on this with Scala meta but this does not cover all use cases) and ensure a smooth and rather uniform transition. A thriving Scala is in our common interest |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
koeninger
May 23, 2017
I've seen significant whitespace in python lead to errors that permanently destroyed end user data.
If you're changing anything about blocks, it should be to require braces even for one liners. It's safer.
koeninger
commented
May 23, 2017
|
I've seen significant whitespace in python lead to errors that permanently destroyed end user data. If you're changing anything about blocks, it should be to require braces even for one liners. It's safer. |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
LPTK
May 23, 2017
My biggest concern is copy-paste friendliness.
If braces are allowed to be used within indent-based code, copy-pasting should not be an issue.
For example, say you want to copy this snippet of code:
// sample code to copy-paste
ls.map with
_ + 1The trick for copy-pasting code while conserving relative indentation is to always include the linebreak above the code snippet before pressing Ctrl-C, so that what you select would be:
// sample code to copy-paste[ <- selection starts here
ls.map with
_ + 1] <- selection ends here
Now, say you want to paste the code in the following program:
class A with
if x > 0 then
println(123)
// want to insert code on the following line:
You only need to press { (modern editors will insert the closing } automatically)...
class A with
if x > 0 then
println(123)
// want to insert code on the following line:
{}And then press Ctrl+V, which would result in valid code:
class A with
if x > 0 then
println(123)
// want to insert code on the following line:
{
ls.map with
_ + 1}The IDE would then have no trouble reformatting this code using the preferred whitespace style, if desired. (Or you can easily do it manually later.)
class A with
if x > 0 then
println(123)
// want to insert code on the following line:
ls.map with
_ + 1
LPTK
commented
May 23, 2017
If braces are allowed to be used within indent-based code, copy-pasting should not be an issue. // sample code to copy-paste
ls.map with
_ + 1The trick for copy-pasting code while conserving relative indentation is to always include the linebreak above the code snippet before pressing Ctrl-C, so that what you select would be:
Now, say you want to paste the code in the following program: class A with
if x > 0 then
println(123)
// want to insert code on the following line:
You only need to press class A with
if x > 0 then
println(123)
// want to insert code on the following line:
{}And then press Ctrl+V, which would result in valid code: class A with
if x > 0 then
println(123)
// want to insert code on the following line:
{
ls.map with
_ + 1}The IDE would then have no trouble reformatting this code using the preferred whitespace style, if desired. (Or you can easily do it manually later.) class A with
if x > 0 then
println(123)
// want to insert code on the following line:
ls.map with
_ + 1 |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
DarkDimius
May 23, 2017
Member
@LPTK, what I'm saying is not that "with proper tooling and by carefully copying proper parts of code it will work", but that you'll have substantially more errors if some-thing doesn't work out. All tools have to agree on how you copy code and how you paste it. All users should also be careful to copy code correctly by following some "tricks", as you called it. I don't think we should add more "tricks" that people should learn.
|
@LPTK, what I'm saying is not that "with proper tooling and by carefully copying proper parts of code it will work", but that you'll have substantially more errors if some-thing doesn't work out. All tools have to agree on how you copy code and how you paste it. All users should also be careful to copy code correctly by following some "tricks", as you called it. I don't think we should add more "tricks" that people should learn. |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
alejandrolujan
May 23, 2017
I feel strongly against this for three main reasons:
- As stated above by others, copy-pasting would very likely be affected by this (until/if IDEs catch up, who knows how long) which is a huge deal IMO.
- I get many complaints from newcomers about the underscore, in the lines of "why does it mean 10 different things depending on context?". Introducing more overloaded terms will negatively impact learning curve IMO.
- I regularly use my IDEs capacity to highlight the block of code surrounded by a pair of curly braces. How long until they catch up to meaningful whitespace and provide similar functionality?
But, fundamentally, I agree with others on the argument that this is not currently a problem at all.
alejandrolujan
commented
May 23, 2017
|
I feel strongly against this for three main reasons:
But, fundamentally, I agree with others on the argument that this is not currently a problem at all. |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
lostmsu
May 23, 2017
@alejandrolujan Number 3 seems to be rather artificial. While it's bad IDEs will have to catch up, they will have to catch up with any syntax change. That should never stop one from happening to begin with.
lostmsu
commented
May 23, 2017
|
@alejandrolujan Number 3 seems to be rather artificial. While it's bad IDEs will have to catch up, they will have to catch up with any syntax change. That should never stop one from happening to begin with. |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
adamw
May 23, 2017
Leaving aside considerations if this would work fine or not, I think that Scala already has too many syntactic options, which might be confusing even for somebody working with Scala for a longer time. Adding more would only add to the confusion, probably with little benefit.
adamw
commented
May 23, 2017
|
Leaving aside considerations if this would work fine or not, I think that Scala already has too many syntactic options, which might be confusing even for somebody working with Scala for a longer time. Adding more would only add to the confusion, probably with little benefit. |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
ryantheleach
May 24, 2017
@lostmsu It's relevant because we are removing 'excess?' information.
Think of the verbosity of Java, that enables IDE's to have sufficient side channel information to correct a whole ton of newbie errors with the syntax, that just won't fly in Scala due to the meaning being far more ambiguous due to the amount of overloaded / optional keywords / different arrangements of keywords, and ultimately more efficient compression of information.
Anything that potentially harms IDE's from correcting and aiding newbies to the language should be avoided.
ryantheleach
commented
May 24, 2017
|
@lostmsu It's relevant because we are removing 'excess?' information. Think of the verbosity of Java, that enables IDE's to have sufficient side channel information to correct a whole ton of newbie errors with the syntax, that just won't fly in Scala due to the meaning being far more ambiguous due to the amount of overloaded / optional keywords / different arrangements of keywords, and ultimately more efficient compression of information. Anything that potentially harms IDE's from correcting and aiding newbies to the language should be avoided. |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
realzeus
May 25, 2017
The advantage of braces is that they provide fully deterministic syntax.
if (condition)
println("something")
action()Currently it will be always with full determinism reformatted to
if (condition)
println("something")
action()- Think about mixes of Space (0x20) and Tab (0x09) characters for indentation
- think that editors allow to define the width of Tab (sometimes 8, sometimes 4, and sometime 2 spaces)
- thing that some editors are configured to automatically replace tabs with spaces while others replace spaces with tabs and everyone has own understanding of tab width
- think about automatic merging in different version control systems
Consider increase of efforts needed to detect and fix these errors.
I have worked with Python enough to say that these errors are very hard to find - every time you have to understand the entire block to check if indentation is correct. It's much more complicated than "missing brace".
realzeus
commented
May 25, 2017
|
The advantage of braces is that they provide fully deterministic syntax. if (condition)
println("something")
action()Currently it will be always with full determinism reformatted to if (condition)
println("something")
action()
Consider increase of efforts needed to detect and fix these errors. I have worked with Python enough to say that these errors are very hard to find - every time you have to understand the entire block to check if indentation is correct. It's much more complicated than "missing brace". |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
Atry
May 25, 2017
I like the with lambda syntax.
@odersky Is it possible to loose the requirement of indentation? It would be very useful for asynchronous programming, for example:
def readFromDatabase(key: String): Future[String] = ???
def shutdownDatabase(): Future[Unit] = ???
def doSomethingWithJava(platformConfiguration: String): Future[Result] = ???
def doSomethingWithDotty(platformConfiguration: String): Future[Result] = ???
readFromDatabase("language").flatMap with language =>
readFromDatabase("version").flatMap with version =>
(
if (language == "Scala" && version == "3.0") {
readFromDatabase("dotty").map with dottyConfiguration =>
doSomethingWithDotty(dottyConfiguration)
} else {
readFromDatabase("java").map with javaConfiguration =>
doSomethingWithJava(javaConfiguration)
}
).flatMap with result =>
shutdownDatabase().map with case () =>
resultThe with lambda syntax can be used as a replacement of async/await
Atry
commented
May 25, 2017
•
|
I like the @odersky Is it possible to loose the requirement of indentation? It would be very useful for asynchronous programming, for example: def readFromDatabase(key: String): Future[String] = ???
def shutdownDatabase(): Future[Unit] = ???
def doSomethingWithJava(platformConfiguration: String): Future[Result] = ???
def doSomethingWithDotty(platformConfiguration: String): Future[Result] = ???
readFromDatabase("language").flatMap with language =>
readFromDatabase("version").flatMap with version =>
(
if (language == "Scala" && version == "3.0") {
readFromDatabase("dotty").map with dottyConfiguration =>
doSomethingWithDotty(dottyConfiguration)
} else {
readFromDatabase("java").map with javaConfiguration =>
doSomethingWithJava(javaConfiguration)
}
).flatMap with result =>
shutdownDatabase().map with case () =>
resultThe |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
realzeus
May 25, 2017
Concerning vertical and horizontal alignment: Scala, Java or Python do not depend on horizontal spacing.
val a=1+2*3
val b = 1 + 2*3
val c = 1 + 2 * 3are all the same. So, we are free to use spaces for emphasizing some code to other readers, not compiler. And compiler's behavior is always deterministic.
We have the same freedom with braces:
- put every closing brace on a separate line
- put all closing braces on a single separate line
- put all closing braces in the end of last meaningful line
With significant indentation it will be prescribed when to put a new line.
It will make map and filter chaining more verbose.
realzeus
commented
May 25, 2017
|
Concerning vertical and horizontal alignment: Scala, Java or Python do not depend on horizontal spacing. val a=1+2*3
val b = 1 + 2*3
val c = 1 + 2 * 3are all the same. So, we are free to use spaces for emphasizing some code to other readers, not compiler. And compiler's behavior is always deterministic. We have the same freedom with braces:
With significant indentation it will be prescribed when to put a new line. |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
LannyRipple
May 25, 2017
Contributor
I wouldn't mind this at all if I could mix and match. For small things I think it would be an improvement but I'd want to be able to fall back to {} where I needed to or desired. I think the proposal for "Significant Indentation" should be changed from
an opening/closing {/} will be implicitly inserted
to
an opening/closing {/} will be implicitly inserted unless found as the next token (which, for an opening brace, will turn off Significant Spacing until it is invoked again)
Mandatory significant spacing makes a program brittle. Without significant spacing I can make the computer work for me to format the program as I like. With significant spacing I have to hand-hold the computer to reach a correct program. (Computers should work for humans and not the other way around.)
|
I wouldn't mind this at all if I could mix and match. For small things I think it would be an improvement but I'd want to be able to fall back to
to
Mandatory significant spacing makes a program brittle. Without significant spacing I can make the computer work for me to format the program as I like. With significant spacing I have to hand-hold the computer to reach a correct program. (Computers should work for humans and not the other way around.) |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
ctoomey
May 26, 2017
I'd be in favor of this for a clean-sheet language, but as others have said, at this point it's only going to make things more confusing for newcomers and introduce more disparity between different developers' and teams' code.
Instead of this, I'd rather see a standard formatting along with a tool to enforce it ala gofmt. Having all Scala code formatted the same would be a lot more beneficial than adding one more way to make it different IMO.
ctoomey
commented
May 26, 2017
|
I'd be in favor of this for a clean-sheet language, but as others have said, at this point it's only going to make things more confusing for newcomers and introduce more disparity between different developers' and teams' code. Instead of this, I'd rather see a standard formatting along with a tool to enforce it ala gofmt. Having all Scala code formatted the same would be a lot more beneficial than adding one more way to make it different IMO. |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
felixmulder
May 26, 2017
Contributor
Thank you all for your feedback. Both me and the rest of the Dotty team have been delighted to see the spirited, and civilized debate on this issue.
One of the key goals of Dotty is simplification, and this of course extends to the syntax of the Scala language itself. The implementation of the proposal as-is, is incredibly straight forward in the parser and scanner but does not require more alterations to other parts of the compiler pipeline - as demonstrated in #2488.
That being said: the proposal as it is now, adds a bit too much leniency to the syntax of Scala.
The language might be better served evolved at a slower pace in a more principled manner.
We're very happy with the different suggestions in this thread and will perhaps draw on them as inspiration in the future. For now - I'm closing this issue as we will not be moving forward with this suggestion as is (also: our inboxes are overflowing).
If this feature, or any other is something that you feel should be further discussed, please open a thread on https://contributors.scala-lang.org/
Cheers,
Felix & @lampepfl/dotty-team
|
Thank you all for your feedback. Both me and the rest of the Dotty team have been delighted to see the spirited, and civilized debate on this issue. One of the key goals of Dotty is simplification, and this of course extends to the syntax of the Scala language itself. The implementation of the proposal as-is, is incredibly straight forward in the parser and scanner but does not require more alterations to other parts of the compiler pipeline - as demonstrated in #2488. That being said: the proposal as it is now, adds a bit too much leniency to the syntax of Scala. We're very happy with the different suggestions in this thread and will perhaps draw on them as inspiration in the future. For now - I'm closing this issue as we will not be moving forward with this suggestion as is (also: our inboxes are overflowing). If this feature, or any other is something that you feel should be further discussed, please open a thread on https://contributors.scala-lang.org/ Cheers, |
felixmulder
closed this
May 26, 2017
lampepfl
locked and limited conversation to collaborators
May 26, 2017
lampepfl
unlocked this conversation
May 26, 2017
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
tr00per
May 26, 2017
Just make Scala look like Haskell: https://www.haskell.org/onlinereport/haskell2010/haskellch10.html#x17-17500010 ;)
But seriously, Layout is cool, IMHO works better than what Python offers.
tr00per
commented
May 26, 2017
|
Just make Scala look like Haskell: https://www.haskell.org/onlinereport/haskell2010/haskellch10.html#x17-17500010 ;) |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
SRGOM
May 26, 2017
I don't use IDEs, I use vim and I like being able to close my eyes and hit ggVG=. Literally the only reason I avoid python is indentation-defined-blocks. It gets really hard to stay confident.
SRGOM
commented
May 26, 2017
|
I don't use IDEs, I use vim and I like being able to close my eyes and hit |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
SRGOM
May 26, 2017
@felixmulder & @odersky Thank you guys for being so open about suggestions and welcoming and accepting negative feedback.
To be clear, while some remarks from people may have been dismissive and off-the-cuff, all of us have huge respect for the scala team and of course, Martin.
SRGOM
commented
May 26, 2017
|
@felixmulder & @odersky Thank you guys for being so open about suggestions and welcoming and accepting negative feedback. To be clear, while some remarks from people may have been dismissive and off-the-cuff, all of us have huge respect for the scala team and of course, Martin. |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
polkovnikov-ph
May 26, 2017
@felixmulder That was the most epic trolling in the PL community I've ever seen: introduce long awaited feature and discard it on the same week. Made me a good chuckle. Thanks!
polkovnikov-ph
commented
May 26, 2017
|
@felixmulder That was the most epic trolling in the PL community I've ever seen: introduce long awaited feature and discard it on the same week. Made me a good chuckle. Thanks! |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
harald-gegenfurtner
May 26, 2017
Let me just repeat this one statement:
Indentation does not scale well.
harald-gegenfurtner
commented
May 26, 2017
•
|
Let me just repeat this one statement: Indentation does not scale well. |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
guizmaii
May 29, 2017
What I love with Scala, it's the confidence I have in the code. This proposal could lower the confidence I could have in my code. So, IMHO, it's not a good path to follow for a language that sell himself as "safe".
guizmaii
commented
May 29, 2017
|
What I love with Scala, it's the confidence I have in the code. This proposal could lower the confidence I could have in my code. So, IMHO, it's not a good path to follow for a language that sell himself as "safe". |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
felixmulder
May 29, 2017
Contributor
I think it's time to lock this now - thanks for the discussion guys! As aforementioned: http://contributors.scala-lang.org for more discussions
|
I think it's time to lock this now - thanks for the discussion guys! As aforementioned: http://contributors.scala-lang.org for more discussions |
odersky commentedMay 20, 2017
•
edited
Edited 1 time
-
odersky
edited May 21, 2017 (most recent)
I was playing for a while now with ways to make Scala's syntax indentation-based. I always admired the neatness of Python syntax and also found that F# has benefited greatly from its optional indentation-based syntax, so much so that nobody seems to use the original syntax anymore. I had some good conversations with @lihaoyi at Scala Exchange in 2015 about this. At the time, there were some issues with which I was not happy yet, notably how to elide braces of arguments to user-defined functions. I now have a proposal that addresses these issues.
Proposal in a Nutshell
If certain keywords are followed by an end-of-line and an indented code block, assume
block structure as if braces were inserted around the indented block. Example:
is treated as equivalent to
Or, for match expressions:
Or, using the new syntax for if-then-else:
Or for for-expressions:
Use
with+ indentation as an alternative way to delimit statement sequences in objects and classes. Example:Also use
with+ indentation as an alternative way to pass arguments that were formerly in braces to functions. Examples:Motivation
Why use indentation-based syntax?
Cleaner typography: We are all used to write
But if we look at it with fresh eyes it's really quite weird how the braces embrace nothing but empty space. Geometrically the braces point away from the enclosed space
.... One could argue that other brace schemes are better. But there are good reasons why the scheme above is the most popular and in a sense arguments how to make braces look less awkward are themselves an indication that braces are fundamentally problematic. It's much simpler and cleaner to get rid of them:Regain control of vertical white space. Most of us are very particular how to organize horizontal whitespace, with strict rules for indentation and alignment. We are much less demanding on vertical whitespace. With braces, we cannot be, because vertical whitespace is fully determined by the number of closing braces. So two definitions might be separated by a single blank line, or by many (almost) blank lines if there are closing braces. One could avoid this by putting several closing braces on one line, but this looks weird and therefore has not caught on.
Ease of learning. There are some powerful arguments why indentation based syntax is easier to learn.
Less prone to errors. Braces are a weak signal, much weaker than indentation. So when
brace structure and indentation differ, we misunderstand what was written. The code below
exhibits a common problem:
Indentation fools us to believe that
actionis executed only ifconditionis true. But of course that's not the case, because we forgot to add braces.Easier to change. A situation like the one above happens particularly often when one adds the first
printlnstatement after the fact. To protect against modification problems like this, some people suggest to always write braces even if they only enclose a single statement or expression. But that sort of boilerplatey defensive programming is generally not considered good practice in Scala.Better vertical alignment. In the most commonly used brace scheme, an if-then-else reads like this:
Instead of nicely aligned keywords, we find weird looking closing braces
}at the beginning of the most important lines in this code. We are all used to this, but that does not make it good. I have recently changed my preferred style to:This solves the alignment issue: The
ifand theelses are now vertically aligned. But it gives up even more control over vertical whitespace.Impediments
What are the reasons for preferring braces over indentations?
Provide visual clues where constructs end. With pure indentation based syntax it is sometimes
hard to tell how many levels of indentation are terminated at some point. In the code below, it is still easy to see that
jis on the same nesting level asg. But if there were many more lines between the definitions, it might not be.The proposal includes with end comments a way to mitigate this issue.
Susceptibility to off-by-one indentation. It's easy to make a mistake so that indentation is off by a space to the left or the right. Without proper guards, indentation based syntax would interprete misalignment as nesting which can lead to errors that are very hard to diagnose.
In the proposal, an indented block is always preceded by a keyword that must be followed by a nested expression or statement sequence. So it is impossible to accidentally introduce nesting by veering off to the right. I tried to experiment with the following additional rule, which would
make it unlikely to accidentally terminate nesting by veering off to the left:
That rule proves to be quite constraining (for instance it would outlaw the chained
filterandmapoperations in the example below), so it is currently not implemented.Editor support. Editors typically have ways to highlight matching pairs of braces. Without that support, it becomes harder to understand the nesting structure of a program. On the other hand,
it's also straightforward to provide navigation help for indentation-based syntax, for instance by providing a command to go to the start of the previous or next definition at the indentation level of the cursor. According to @lihaoyi's comment, major editors do something like this already.
But neither of these points are the strongest argument against indentation. The strongest argument is clearly
Proposal in Detail
Expanded use of
withWhile we are about to phase out
withas a connective for types, we propose to add it in two new roles for definitions and terms. For definitions, we allowwithas an optional prefix of (so far brace-enclosed) statement sequences in templates, packages, and enums. For terms, we allowwithas another way to express function application.f with { e }is the same asf{e}. This second rule looks redundant at first, but will become important once significant indentation is added. The proposed syntax changes are as described in this diff.Significant Indentation
In code outside of braces, parentheses or brackets we maintain a stack of indentation levels. At the start of the program, the stack consists of the indentation level zero.
If a line ends in one of the keywords
=,if,then,else,match,for,yield,while,do,try,catch,finallyorwith, and the next token starts in a column greater than the topmost indentation level of the stack, an open brace{is implicitly inserted and the starting column of the token is pushed as new top entry on the stack.If a line starts in a column smaller than the current topmost indentation level, it is checked that there is an entry in the stack whose indentation level precisely matches the start column. The stack is popped until that entry is at the top and for each popped entry a closing brace
}is implicitly inserted. If there is no entry in the stack whose indentation level precisely matches the start column an error is issued.None of these steps is taken in code that is enclosed in braces, parentheses or brackets.
Lambdas with
withA special convention allows the common layout of lambda arguments without braces, as in:
The rule is as follows: If a line contains an occurrence of the
withkeyword, and that same line ends in a=>and is followed by an indented block, and neither thewithnor the=>is enclosed by braces, parentheses or brackets, an open brace{is assumed directly following thewithand a matching closing brace is assumed at the end of the indented block.If there are several occurrences of
withon the same line that match the condition above, the last one is chosen as the start of the indented block.Interpreted End-Comments
If a statement follows a long indented code block, it is sometimes difficult as a writer to ensure that the statement is correctly indented, or as a reader to find out to what indentation level the new statement belongs. Braces help because they show that something ends here, even though they do not say by themselves what. We can improve code understanding by adding comments when a long definition ends, as in the following code:
The proposal is to make comments like this one more useful by checking that the indentation of the
// endcomment matches the indentation of the structure it refers to. In case of discrepancy, the compiler should issue a warning like:More precisely, let an "end-comment" be a line comment of the form
where
<id>is a consecutive sequence of identifier and/or operator characters and<id>either ends the comment or is followed by a punctuation character.,;, or,. If<id>is one of the stringsdef,val,type,class,object,enum,package,if,match,try,while,do, orfor, the compiler checks that the comment is immediately preceded by a syntactic construct described by a keyword matching<id>and starting in the same column as the end comment. If<id>is an identifier or operator name, the compiler checks that the comment is immediately preceded by a definition of that identifier or operator that starts in the same column as the end comment. If a check fails, a warning is issued.Implementation
The proposal has been implemented in #2488. The implementation is quite similar to the way optional semicolons are supported. The bulk of the implementation can be done in the lexical analyzer, looking only at the current token and line indentation. The rule for "lambdas with
with" requires some lookahead in the lexical analyzer to check the status at the end of the current line. The parser needs to be modified in a straightforward way to support the new syntax with thegeneralized use of
with.Example
Here's some example code, which has been compiled with the implementation in #2488.