Walter Bright - Programming Language Ideas That Work and Don't Work [video]
youtube.com
youtube.com
for (int i = 0; i < 10; ++i);
{ ...
I agree with him on all points, though I'm not sure about warnings. I think most people are against warnings because they've been traumatised by C/C++ where a ton of the warnings really should be errors (my favourite is -Wreturn-type which can cause insanely difficult to debug issues if omitted). Any sane C++ project has to turn on `-Wall -Werror` because so many of the warnings are really "backwards compatible errors".So warnings are a disaster in C++ but I don't think that means they fundamentally are. Warnings in Rust seem to be fine.
And having no warnings at all is more annoying than Rust's approach. Go won't let you have unused variables - which is an indication of mistakes - but is really annoying when you're writing incomplete code.
That's kind of my point. C/C++ have a really bad implementation of warnings, which leads people to think that warnings are fundamentally a bad idea, which is not something I am convinced by.
And frankly I'd rather the build not stop on the first error. In fact it would be best if the build system tried to link even if there are errors. Might work.
I agree, but I don't think they should be required to fix all warnings before checking in code, which is what no-warnings languages like Go require.
> And frankly I'd rather the build not stop on the first error. In fact it would be best if the build system tried to link even if there are errors. Might work.
Not sure what your point is here. Most compilers don't stop on the first error.
First of all, languages with less legacy constraints can have the line between warnings and errors in the right place, so users don't have the need to turn all warnings into errors. Werror can be reduced from being the best practice to just overly cautious pedantism.
Then, even Werror can be made more robust. Rust solves this in a few ways:
• cap-lints: it suppresses its Werror equivalent when building someone else's code (dependencies), so the code you can't fix yourself won't break on you due to just a lint.
• editions: Bigger language changes that could render old code incompatible are gated behind the edition setting, which is an opt-in to new stuff, including some new warnings.
• `allow(warning_id)`: you can put it on any code block or even the whole project. So even if you get a new warning that is a false positive or just don't have time to deal with it, you can reliably and precisely turn it off in the source code (as opposed to compiler-specific flags, #pragmas, or reshuffling the code until the warning goes away).
• clippy: all the weaker, experimental, and more opinionated warnings are in a separate tool. The compiler proper adopts clippy's warnings only if they prove to be useful. The type of people who use Werror use clippy, so they'll likely have their codebase clean of these warnings before they make it to the compiler.
I've seen his comments here quite a bit, It's good to put a face with a name. 8)
As far as compiler flags, that's an interesting take on things. From the meta perspective, not making a decision but putting in a feature switch is essentially kicking a can down the road, with all the grief that eventually entails.
To be true it doesn't need to be impossible to write code in a language with implicit declaration, it just needs to be significantly worse.
Proof that Python doesn't have implicit declaration:
>>> foo
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'foo' is not defined
Pyhon's design sin is that it fails to distinguish the binding of a variable from assignment. The programmer cannot locally (i.e. just in that statement alone) express "I expect this statement to be freshly creating a variable, introducing it into the scope, with value 42" and "I expect to be assigning value 42 to a variable which already exists in the scope (or else this should be diagnosed)".An example of a language actually without declaration is Awk. This helps one-liners be very terse, but hinders large programs.
Proof that awk has implicit declaration in contrast to Python:
$ awk 'BEGIN { print foo }'
$
No errors. The shell has implicit declaration: $ echo $foo
You turn it off in serious scripts with "set -u" to have it diagnosed.I'm not entirely sure this is a bad thing? I'm learning Go right now, and I'm encountering plenty of code like:
err := DoFoo()
if err != nil ....
err = DoBar()
if err != nil ....
The problem with that is that if you remove the DoFoo line you have to change the DoBar line, even though nothing's really changed there. An (albeit small) waste of time, an extra thing to think about which has nothing to do with the problem you're solving, more lines to review in the PR, and more chance of merge conflicts later. if err := DoFoo(); err != nil { ...
The 'err' variable will be local to the 'if' so you can write also : if err := DoBar(); err != nil { ...
It is also nice to write it in one line :-) err := DoFoo();
if err != nil ...
err := DoBar()
if err != nil ...
DoFoo and DoBar are different functions; their error return might not even be the same type.If mixed declarations and statements are not supported:
{
err := DoFoo();
if err != nil ...
}
{
err := DoBar()
if err != nil ...
}
this is what I do in C to reduce the scope of variables, and it has the benefit over
the former example in that we delimit where the scope begins and ends. We know that
after the closing brace, nothing can be referencing that err variable.
(I'm not a fan of mixed decls and statements).Modern languages should support binding a variable in if:
if (err := DoFoo()) {
// err scoped here
}
My first example would be "bad" according to David Betz because it perpetrates shadowing.So if x is unbound, x + x evaluates to 2x
And if x = 2, x + x evaluates to 4
But this means that it's hard to catch typos!
AFAIK Mathematica works like this.
a) use sigils to signal that some variable should be treated as an abstract values for CAS purposes. like, :x + :x rather than x + x (or.. use sigils to signal that a variable is "normal", like $x is a normal variable and x + x is CAS stuff)
b) declare CAS variables as such before using them. like with a "var" keyword or something. optionally, declare normal variables, but with a different keyword
Note that formal math always look like "b)" (we declare our variables before using them), it's informal math that looks what Mathematica is doing
That's exactly what he means by "implicit declaration" though - he even calls out Python as an example.
Python does have implicit declaration, it is just slightly more limited than languages like Bash and CMake - it only implicitly declares variables when you write to them, not when you read them.
Anyway, regardless of the semantics I think we all agree that all forms of implicit variable declaration are a bad idea.
There is nothing implicit about x = 42 in Python. You wrote it explicitly.
The language is not pretending that you invisibly defined x somewhere at the top and are now assigning it.
The most familiar "implicit declaration" to everyone is the feature in traditional C (that any good C compiler warns about and was banished in C++ early). Like that you can call a function without declaring it:
puts("Hello"); /* no <stdio.h> included */
The compiler looks at the argument types and deduces a declaration. The return type is assumed to be int, and since the "Hello" expression decays to char * type, the implicit declaration is int puts(char *).Python actually doesn't have declarations at all (except when we consider the new typing features).
When we introduce something into a scope without giving any attributes about it like type, that's a definition. A declaration is something which makes facts known about an identifier: like whether it is a constant, does it have a type, and such. It doesn't necessarily make it exist.
In Lisp declarations can be added about an existing identifier:
(let ((x 3)) ;; definition of x
(declare (type (integer x))) ;; declaration about x
...)
In languages focused on static type, definitions declare. E.g. in C, all definitions (other than of macros) are declarations; not all declarations are definitions.In Python, a variable must be visibly defined before it is used, quite clearly; it doesn't have implicit definition. Untyped Python has no declarations.
You wrote the assignment explicitly. You did not write any declaration that "a variable x should already exist" anywhere did you?
> Betz should fix his terminology to align with the industry.
His terminology is aligned with the industry. Google it. Here are the top two results for me:
* https://www.quora.com/What-does-the-explicit-and-implicit-de... * https://stackoverflow.com/questions/56999347/variable-declar...
> Like that you can call a function without declaring it:
That's exactly the same as your Python example!! "There's nothing implicit about `puts("Hello");`. You wrote it explicitly."
> When we introduce something into a scope without giving any attributes about it like type, that's a definition. A declaration is something which makes facts known about an identifier: like whether it is a constant, does it have a type, and such. It doesn't necessarily make it exist.
No. A declaration can only declare that a variable exists. Consider this in Javascript:
let foo;
Not "having" declarations and declarations being implicit are exactly the same thing. You're getting tripped up over your language.Yes; that's the only correct thing in your comment.
A declaration can declare that a variable exists, and nothing more, which is useful in some language that has a strict rule that variables must have been indicated as existing before they can be used.
If a declaration causes the variable to exist, then it's a definition.
However, about that specific example, "let foo;" in Javascript is actually a definition. It causes foo to exist in the scope where it appears, with a value of undefined.