When you get an error there's a verbose explanation you can ask for, which describes the problem, gives example code and suggests how you can fix it. The language has a longer ramp-up period because it contains new paradigms, so little touches like this help a lot in onboarding new devs.
To be fair, if decorating the error with information about a filename is what you needed, since Rust's Error types are just types nothing stops you making your function's Error type be the tuple (std::io::Error, &str) if you have a string reference, or (though this seems terribly inappropriate in production code) leaking a Box to make a static reference from a string whose lifetime isn't sufficient.
Traceback (most recent call last):
File "calculation.py", line 54, in <module>
result = (x / y / z) * (a / b / c)
~~~~~~^~~
ZeroDivisionError: division by zero
In this new version it's now obvious which variable is causing the 'division by zero' error.https://docs.python.org/3.11/whatsnew/3.11.html#enhanced-err...
Stack trace is useful, especially in understanding how the code is working in the system.
But if the goal is solve the problem with the code you've been working on, existing traces are way too verbose and if anything add noise or distract from getting to productive again.
I could see tracebacks get swifty terminal UI that shows only the pinpointed error point that can be accordion'd out to show the rest.
But: it's kind of a virtue that the most important error message comes last. Far too many (ahead of time) compilers output too many errors. The most likely error to matter is the first one, but it is often scrolled way off screen; the following errors might even just be side effects that mislead or confuse the new users.
The old Python stack trace, circa 2015, was the best in the world.
The new ones are even better.
The gap between Python and Rust, and the JVM languages/C/C++, is increasingly widening.
Stack trace is one of the areas Python and Rust are making the case for why they're the languages of the future.
Clang and GCC error messages have come a loooong way in the last 10 years or so and their quality is impressive.
To be fair, I'm currently stuck at python 3.6 ATM and I hear that python has also improved a lot since.
Although, personally, I enjoy python list comprehensions.
[x for x in y if x is not z]
Sometimes though, facilitated by Jupyter's notebooks making executing as you build the code very easy, I create a beast like this: [os.path.join([x for x in y if x is not z]) + '/bin' for a in b if 'temp' not in a]
Yes this is fictional and probably contains an error but you get the point, you can filter lists of lists like this, but it's really unfriendly to anyone trying to understand it later.Anything more will come back to bite you later.
That's one expression because it used to be part of a giant comprehension, but I moved it into a function for a bit more readability. I'm considering moving it back just for kicks though.
My philosophy is: if you're only barely smart enough to code it, you aren't smart enough to debug it. Therefore, you should code at your limit, to force yourself to get smarter while debugging
tags = list(set([
nel
for subli in [
mel
for subl in [
[[jel.split('/')[2:] for jel in el]
for el in classified
]
for mel in subl
]
for nel in subli
if nel
]))
Still not very readable, tho. But that's largely due to Python's outputs-first sequence comprehension syntax being a mess that doesn't scale at all.Side note: one other thing I always hated about those things in Python is that there's no way to bind an intermediary computation to a variable. In C# LINQ, you can do things like:
from x in xs
let y = x.ComputeSomething()
where y.IsFoo && y.IsBar
select y
In Python, you have to either invoke ComputeSomething twice, or hack "for" to work like "let" by wrapping the bound value in a container: y
for x in xs
for y in [x.ComputeSomething()]
if y.IsFoo and y.IsBar
It's not just about not repeating yourself or not running the same code twice, either - named variables are themselves a form of self-documenting code, and a sequence pipeline using them even where they aren't strictly needed can be much more readable.Choices like wrapping a set constructor around a list comprehension rather than just using a set comprehension don't help, neither does using multiple nested no-transformation comprehensions laid out that way.
By hand, on mobile, I think this reduces to (assuming “from itertools import chain” in the file header):
tags = list({
nel
for subli in chain.from_iterables(chain(
[[jel.split('/')[2:] for jel in el]
for el in classified))
for nel in subli
if nel
}) ys = (x.ComputeSomething() for x in xs)
result = [y for y in ys if y.isFoo and y.isBar] y
for x in xs
if (y:=x.ComputeSomething()).IsFoo and y.IsBar[(y:=x.ComputeSomething()) for x in xs if y.IsFoo and y.IsBar]
[x for stop in range(5) for x in range(stop)]