> Redundancy. Yes, the grammar should be redundant. You've all heard people say that statement terminating ; are not necessary because the compiler can figure it out. That's true — but such non-redundancy makes for incomprehensible error messages. Consider a syntax with no redundancy: Any random sequence of characters would then be a valid program. No error messages are even possible. A good syntax needs redundancy in order to diagnose errors and give good error messages.
This makes no sense. A terminator is required, multiple terminators (ie, redundancy) are not. Various modern languages eliminate redundancy: any random sequence of characters is not a valid program in those languages.
Disappointed Dr Dobbs would publish something like this.
More subjectively:
> Tried and true. Absent a very strong reason, it's best to stick with tried and true grammatical forms for familiar constructs. It really cuts the learning curve for the language and will increase adoption rates.
More people will program in the future than who do at present. If you aim to approach this audience, things like using '=' to set values, rather than test equality, make no sense to the vast majority of those people.
The lack of a terminator in python, and the optional terminator in JavaScript, has often produced weird errors for me, when the compiler and me disagree on if something should be interpreted as one line or two. With a semicolon, it's clearer.
Of course, missing semicolon errors are still annoyingly bad, I wish GCC and clang could produce better errors messages in this case.
Yet, semantic white space creates almost no problem in Haskell. Both Python and JavaScript suffer from a bad designed feature, it's not inherent on the use of white space.
I've had similarly weird errors in python, but given the same situations its ever occurred to me in ruby. Maybe its just the way the grammar is defined, for example stuff like the following works perfectly fine in ruby while python just says "Invalid Syntax":
Of course, the point I was making is that it's perfectly possible to write a parser that understands newline as a terminator without sacrificing these idiosyncrasies.
I think statistically humans are more likely to encounter errors when the machines use different terminators than humans do. But we could always test and find out for sure.
"A terminator is required, multiple terminators (ie, redundancy) are not."
Food for thought and discussion, rather than a "correction": Terminators are not required in a language. See the concatenative languages like Forth or Factor, in which
z = func1(x, y);
func2(1 + 2 + 3, z);
comes out looking like
1 2 + 3 + x y func1 func2
And, indeed, many people find this a fairly confusing programming style. (For instance, note how the second expression has no "z" variable in it.) Functions tend to become difficult-to-differentiate streams of tokens with few breaks in them. You'll also note that by token count the concatenative approach is smaller (21 non-whitespace tokens in the first if I'm counting this correctly, 9 tokens in the second); that is normal, not a fluke. By using the stack to hold things almost all variable assignments and their usages end up going away. However, despite being a programming paradigm that nearly as old as imperative programming, it has not taken the world by storm.
Speaking formally, we might consider a language to contain "redundancy" if there are source programs which produce the same compiled output; if they did it would be "redundant". Since all syntactically erroneous programs produce the same output (presumably none -- obviously I'm excluding e.g. diagnostic error messages here), a language which has more than one syntactically invalid program is redundant.
This might not seem all that useful in a practical sense, but I think that's basically the author's point: Such a language would be essentially impossible to write, and thus you're always going to have redundancy. As a result, you should evaluate redundancy in practical terms, rather than viewing it as always needing to be removed.
Natural languages are full of redundancy, which makes them easier to understand because we can do more accurate error correction. For example, the phonotactics of a language define which sounds may appear in conjunction with which other sounds; if someone says something that appears to contradict these rules, whether because they mispronounced it or because you misheard it, you have more ability to infer what they meant than if the error were a valid-but-different utterance.
It works the same for programming languages—when designing a notation, you need to consider how you’re going to produce useful error messages when people make common errors, and the simplest way to do that is to add a bit of redundancy so you can infer the programmer’s intent.
For example, I’m working on a dataflow-type language where the syntax for introducing local variables is evocative of a labelled edge in a graph; the original syntax to introduce three variables was this:
-> x y z;
But the problem was that people would forget the semicolon, so this notation would “run away” and continue to gobble up any following identifiers:
// Whoops, accidentally declared 6 variables
-> x y z
foo bar baz
{ … }
The solution? Add commas between the identifiers:
-> x, y, z;
Now if someone forgets a semicolon:
-> x, y, z
foo bar baz
{ … }
The compiler can say “I expected a comma or a semicolon, not this identifier ‘foo’” and additionally use the newline as a hint to offer “I suggest putting a semicolon after ‘z’”.
Likewise, in C-style languages you have to “redundantly” specify the number of arguments to each function at each call site using commas:
foo(1, 2, 3)
In Haskell, for example, that would be written like so:
foo 1 2 3
I find this prettier—it’s less redundant and confers other advantages. But it also suffers from the drawback that now the compiler has to figure out when you have an argument-count mismatch using the types rather than the syntax, making it harder to produce good error messages. The commas are an extra hint to the compiler about how many arguments you intended to pass.
Redundancy increases the the ability to detect errors, like a checksum in a code.
take foo("one", "two") mis-typed as foo("one" "two"),
instead of getting a "missing comma" error, you get "wrong number of arguments", unless the function can take one argument, in which case you may get some other random error.
> If you aim to approach this audience, things like using '=' to set values, rather than test equality, make no sense to the vast majority of those people.
A lot of good that will do if your language doesn't live long enough for a critical mass of those people to keep it alive. shrug
Python was designed for new programmers, rather than existing programmers. Ruby and JavaScript also broke many existing conventions. All have had enormous success.
I'm not sure what point you're trying to make. Python was plenty familiar to programmers of the day, and there's a good argument that its success is only attributable to its English-like syntax insofar as it caused adoption into CS curricula (rather I think the lion's share of its success is due to its status as an early, cross-platform, approachable scripting language). Further, Ruby is successful solely because of Rails and JavaScript solely because of it's browser monopoly; not remotely because of any broken conventions.
My point isn't that novelty is antithetical to success, but that it's a terrible mistake to assume that 1) programmers of the future will be inherently familiar with math syntax and 2) these programmers at any point in time will significantly outnumber existing programmers such that the market dynamics favor laypersons over trained programmers.
> 1) it's a terrible mistake to assume that programmers of the future will be inherently familiar with math syntax
That's an excellent point, and more of an expansion of the argument than a retort! Perhaps math syntax is simply wrong and = has no place in either setting values or comparing them.
> 2) these programmers at any point in time will significantly outnumber existing programmers such that the market dynamics favor laypersons over trained programmers.
Java dominates CS introductions before Python did. Python would not have appealed to existing Javanauts. Yet Python now dominates CS introductions and many areas of programming. The market favoured a language that trained programmers did not.
> That's an excellent point, and more of an expansion of the argument than a retort! Perhaps math syntax is simply wrong and = has no place in either setting values or comparing them.
The issue isn't math syntax; it's comparing the population of all future novice programmers with the population of current programmers. The apt comparison is all future novice programmers who might be exposed to this hypothetical language to all experienced programmers who might be exposed to this language. Note that an "experienced programmer" might be someone born in 2018 who happened to have learned JavaScript2035 before seeing our new hypothetical language. The latter likely pool dwarfs the former. And this doesn't factor in that a language is a living thing--it needs a critical mass of users in order to survive, and the largest pool of potential users is existing programmers (very, very few early language adopters are first time users, I would wager). I think this is the retort I meant to make; I don't think this is merely an expansion of the topic, with sincere respect. :)
> Java dominates CS introductions before Python did. Python would not have appealed to existing Javanauts. Yet Python now dominates CS introductions and many areas of programming. The market favoured a language that trained programmers did not.
Apologies, I don't see how Python displacing Java in CS courses supports your point (or harms mine). Please elaborate.
In the case of JS, JS was designed to require a semicolon to terminate statements, then ASI was hacked on when programmers realized they were already adding newlines. ASI is a bad idea because it's not consistent (that said, ugly code is more likely to trigger ASI issues than otherwise).
Python was designed so there's a single terminator used by both humans and machines.
This makes no sense. A terminator is required, multiple terminators (ie, redundancy) are not. Various modern languages eliminate redundancy: any random sequence of characters is not a valid program in those languages.
Disappointed Dr Dobbs would publish something like this.
More subjectively:
> Tried and true. Absent a very strong reason, it's best to stick with tried and true grammatical forms for familiar constructs. It really cuts the learning curve for the language and will increase adoption rates.
More people will program in the future than who do at present. If you aim to approach this audience, things like using '=' to set values, rather than test equality, make no sense to the vast majority of those people.