I know this is probably a point of contention, but -- I find it takes work to parse code like this:
proc sumTillNegative(x: varargs[int]): int =
for i in x:
if i < 0:
return
result += i
I have a hard time reading this code, because there is no indication of where any block ends.
This is a very simple example - when things are more deeply nested, and when several locations have multiple levels of un-indentation happening in a single line, it gets really tricky to parse.
The following is much easier for me to read:
proc sumTillNegative(x: varargs[int]): int {
for i in x {
if i < 0 {
return
}
result += i
}
}
It's also easier (faster) for a machine to parse, probably.
It's also safer to edit and refactor, since it's clear where each block begins and ends, and the parse can bark about mismatching braces.
It's also easier to inspect in an editor, where matching braces can be highlighted.
I know it's more ceremonious with braces, but besides that, what are the arguments in favor of this somewhat brittle syntax?
I don't want to start a war about it, it's just that I have used CoffeeScript in the past, and I (and coworkers) had some negative/confusing experiences reading/writing code with indentation-based syntax - code would frequently come out valid (e.g. compiles and runs) but unexpected behavior would occur when somebody un/indented a portion of code to the wrong level, effectively "moving around" the "invisible braces", in some cases leading to errors that were very hard to correct because one author was unsure about which lines had been moved by another author and how far they needed to be moved.
I'm all for brevity and low ceremony, I'm just concerned about the practical impact in a multi-developer scenario with source-control tools etc.
I'm just trying to understand the advantages - I take it there must be advantages of indentation-only that outweigh the potential dangers and the advantages of braces, I just don't know what they are?
I'll attempt an answer. What are the advantages?
Fortunately Nim is not treading an unknown path. Indentation has been used instead of braces for many years. There have been many debates on this topic elsewhere:
http://c2.com/cgi/wiki?PythonWhiteSpaceDiscussion
Personally, I've used Python for many years and have never had trouble with many hands on the same piece of code. I believe that a bug resulting from bad indentation is just as likely as a bug resulting from a misplaced brace. Remember that changing the indentation of a random line of code is most likely going to be a syntax error (at least in Python). Because this has been discussed elsewhere, I don't know how much more I want to say about it here.
As for Nim, it's working the way Araq indented it. :)
The following works, although it seems that it's not
possible to put return or break in parenthesis.
proc sumTillNegative(x: varargs[int]): int = (
for i in x: (
if i < 0: (
discard 1;
);
result += i;
)
)
> It's also easier (faster) for a machine to parse,
> probably.
A little, but it's something like 20 extra instructions.
> I'm just trying to understand the advantages - I take
> it there must be advantages of indentation-only that
> outweigh the potential dangers and the advantages of
> braces, I just don't know what they are?
For me, it's just that it looks nicer. I've never had
problems with indentation aspect of the language.
Python has faced the same criticism, and the arguments
they make should be equally applicable to Nim. A link I
found from a brief search:
<https://wiki.python.org/moin/Why%20separate%20sections%20by%20indentation%20instead%20of%20by%20brackets%20or%20%27end%27>
I have a hard time reading this code, because there is no indication of where any block ends.
There's not much difference with the braces version, but most likely your editor will highlight them. Find the option of your editor which highlights blocks of code based on indentation level. IIRC both Microsoft Visual Studio and Eclipse do this in some code folding preferences category. Then your editor will shade blocks slightly differently.
I'm all for brevity and low ceremony, I'm just concerned about the practical impact in a multi-developer scenario with source-control tools etc.
Python has been used successfully in numerous multi-developer projects so this argument is weak. Perhaps it's just a matter of getting used to? (Even brace syntax has its own gotchas, particularly because the indentation may not match the brace and the reader infers more from indentation than counting braces).
I'm not quite sure how indenting to the wrong level would even compile. Every line of a block must be indented to the same level, must be indented (i.e cannot be the same level as the block 'header' statement), and blocks are contiguous. Maybe it was a coffeescript specific issue?
I think it's more about the width of indentation
Nim's source code filter allows to replace tabs with any number of spaces, thats works fine.
Personally I prefer tabs for indentation: Easy adjustable indent, less keystrokes, smaller uncompressed file size. Of course I know about the problems when mixing tabs and spaces for indentation. Editor configuration (multiple spaces for one tab and some smart indent) may help indeed.
@vbtt: Semantically wrong by bad indentation code can compile
if killEveryone:
armWarheads()
launchMissiles()
vs
if killEveryone:
armWarheads()
launchMissiles()
where the second block launches unarmed missiles. The Go designers complained about some such problem in their overly brief rationale as to why they didn't go with offside rule syntax, but I never saw the problematic examples they complained about.
That said, I'm a heavy user of the boring stack (Python/Java/C++) and the thing I like the most about Python is the syntax. Less clutter, more readable, and in practice the problems described just haven't been real problems. After F# made OSR optional, almost all users switched to that (from OCamlish syntax) and now it's the default.
I only use spaces, never tabs, but in my editor of choice (Emacs, I'm an old guy) tabs auto align and then move to other alignments on repeats. Some kind of block shading like @gradha describes might be useful too. I really like the syntax of Nim. I like Lisp syntax too; however when Lisp dude David Moon set out to design his "Programming Language For Old Timers" (PLOT) http://users.rcn.com/david-moon/PLOT/ you can see that he went with OSR instead of s-expr based syntax.
I agree on all of your points, but it would be sad if backward compatibility dictated so strongly what changes could be made even for a pre-1.0 language with a small user base.
The problem with that line of argumentation is that we never reach a version 1 (there is always something that can be improved) and so we never attract a critical mass and so the argument "not many use it, so let's break it" continues to hold forever... not nil should be the default, the effect system can track lifetimes so we can get rid of the GC or mitigate the need for it, the codegen should be based on LLVM, forward declarations should be a thing of the past ... There is always something.
Araq: The problem with that line of argumentation is that we never reach a version 1
I'm well aware, and right now on the side of the 'don't break it, just fix bugs for a while' crowd, but this nit can be addressed by warnings, right?
BTW, nice list of arguments for breakage! I laughed, it's like you read my mind for features I want, but all of those are pretty heavy and not addressable by adding warnings.
I'm not particularly concerned about result and return being mixed, though. That's more an issue for a code review process.
Code-reviews can also catch uninitialized values, so I'm not sure what problem you see anymore.
I understand that not everyone sees result+return as a problem. What I'm saying is this:
If mixing result and return VAL were discouraged, then the problem of uninitialized result would be much easier for a linter to detect. Just report the complete absence of both result-assignment and return VAL. That would catch the common mistake and would never report a false positive.
The path forward would be to add only a warning for mixed result and return VAL. That's enough. I'd clean up the standard library myself. Nothing would fail, but people would be encouraged to avoid the mix. Later, a simple linter would find most likely bugs.
And since a warning is enough, this path could wait until post-1.0.
I understand that not everyone sees result+return as a problem.
Then why to ask for adding warnings for this...
And in any way: if absence of explicit result is a problem, then why not to add the warning for that?
cdunn2001: Code-reviews can also catch uninitialized values, so I'm not sure what problem you see anymore.
Mixing result and return can be discovered purely by pretty trivial syntactic analysis. An uninitialized result is often a subtle semantic problem (example: the result only gets set inside a while loop, but it's possible that the body of the loop is never executed because the condition is false the first time around).
@Jehan wrote:
An uninitialized result is often a subtle semantic problem (example: the result only gets set inside a while loop, but it's possible that the body of the loop is never executed because the condition is false the first time around).
I see now. You are concerned about a completely different issue. I assumed something based on your choice of words, but I see now that I was wrong. result is always initialized (to its default value, as can be verified by viewing the C code) but it might not be assigned explicitly (i.e. "initialized" per your definition) by the programmer. Yes, that is a much harder problem to discover.
I'm highlighting this by @cdunn2001:
This is not an issue of indentation. It's a matter of reasonable safety.
And this by @brianrogoff:
it would be sad if backward compatibility dictated so strongly what changes could be made even for a pre-1.0 language with a small user base
Although I'm also going to second what @Araq said:
The problem with that line of argumentation is that we never reach a version 1 (there is always something that can be improved) and so we never attract a critical mass and so the argument "not many use it, so let's break it" continues to hold forever
I get it - day late and a dollar short, curly braces might create conflicting syntax, a major BC break is bad for community.
In my opinion though, the introduction of an optional "end" keyword is a poor alternative to braces - it would seem like an ugly symptom of realizing too late that the language needs block delimiters after all.
Personally, I'd rather have no delimiters than optional "end" keywords here and there making the code inconsistent and harder to read.
I guess I'd say, either stick with the line of reasoning where block delimiters are of no value - don't add them. Or, accept the fact that it's going to require a BC break.
Dirtying up the language with noise like optional delimiters, in my opinion, is not a good trade-off - personally I'd rather do entirely without them.
I wasn't arguing against indentation sensitivity (as Jehan said, there are arguments for and against, I'm used to both) or for the inclusion of optional. The only reason is that I remember having made this mistake and it took me a small amount of time to find it. I'd be satisfied with a lint rule; however even a compiler warning would not break existing code.
At the same time, while I grasp Araq's point about stability, it would be a shame if nothing at all could be changed in Nim (method, I'm looking at you!) for fear of breaking backwards compatibility. When Nim is at 1.0, then that will change, because with 1.0 comes an explicit promise of stability.
In my opinion though, the introduction of an optional "end" keyword is a poor alternative to braces - it would seem like an ugly symptom of realizing too late that the language needs block delimiters after all.
The initial design of Nim was envisioned as an AST with multiple "skins" so programmer A can have it his way and still use programmer B's module directly rather than having to interface with a completely different programming language. And after 10 years this simple idea is still ahead of its time with the closest implementation for this idea being .NET. (Arguably Mathematica is already there ...). But eventually the last programmer will realize that code is a tree moreso than a stream of bytes and then its time will come.
No matter whether we introduce optional braces or not, "realizing too late" couldn't be father away from the truth. ;-)