Python features that can be removed
Python is more than thirty years old at this point and has accumulated some cruft. A new language has the opportunity to remove some of it.
String formatting
At times a new and better way of doing things was found, but the old feature
was entrenched and could not be removed, so we ended up with both of them at once.
For instance, f-strings can replace all uses of old-style formatting expressions and string
concatenation. This expression using the % operator:
"cost: %d" % (price * quantity)is equivalent to this:
f"cost: {price * quantity}"The concatenation operator (+) is also redundant. The expression:
"Received " + currency + " " + str(price)can be replaced by:
f"Received {currency} {price}"While dropping these features will make porting code more work, it also means you never
have to see ugly % expressions again.
Whitespace
I dislike seeing blank space to the left of colons and commas, and part of me wants to ban it in the parser.
If Newton takes off, some of the users are likely to come from Haskell and Scala,
and in those languages colon is viewed as an operator instead of punctuation,
as it is used in prose. Some of them will no doubt write foo : Int instead of foo: Int, if the parser accepts it.
I know what you are thinking–just provide a formatter to standardize the style automatically–and that is a good idea. On the other hand, code that does not compile does not spread!
How to decide
I think the heart of the matter is who bears the cost–the individual, the team, or the community as a whole? The wider the range of impact, the stronger the case for addressing it in the language. For instance, unconventional use of whitespace is unlikely to spread to other organizations, so I should probably suppress the urge to enforce my preferences in the parser.
String formatting is similar in that both problems concern readability; and people who think of colon as an operator probably occur about as frequently in the population as programmers writing old-style Python. But fewer teams will use tools to standardize formatting expressions than whitespace, so it is more likely to remain over time.
Dangerous assignments
Some features are not redundant but dangerous, and we want to find a safer form of it. Iterable unpacking is such a case:
a, *b, c = fooIt looks innocuous, but it can raise exceptions, run out of memory, or loop forever.
In Newton, the above will only pass the type checker if foo is known to have at least two elements.
None is not an option
One of the most common sources of exceptions in Python is attempting to call a method on None. It is hard to protect against without littering the code with conditionals, since almost anything can be None.
Newton replaces None with option types. Where the type indicates a value may be missing, the compiler requires you to check first. Otherwise, it is guaranteed to exist!
Hidden dependencies
In Python, loading a module executes the top-level statements found inside. Because of this,
rearranging the import statements can change a program’s behavior.
If it does, there is a hidden dependency somewhere, and relying on it makes the program brittle.
That is why import should be a compile-time declaration and not an effectful computation.
Other top-level statements should be banished to a main function.
Granted, global constants is a useful feature to have, and they may need to be initialized. But the expression on the right hand side should be pure to ensure the result is consistent regardless of which module is loaded first.
Let it be
I also want to make a clear distinction between declarations and assignments.
In Python, foo = 1 can either introduce a new binding,
or assign a new value to an existing variable.
In Newton, local bindings have to be declared using the let keyword. This fits better with
a statically typed language where bindings can have type annotations, but assignments cannot.
A related question concerns the scoping rules. Variables in Python have function scope,
which means they are accessible throughout the function, once created.
This does not actually cause a lot of problems in my experience, so I hesitate a bit.
But block scope does avoid some problems, and the let keyword will highlight
to users that Newton differs from Python in this regard.
Stefan