I get that it's not the shiny new thing, but I don't understand people hating on it. Is this just junior devs who never learned it, or is there some new language out that I missed? (And please don't tell me Javascript....)
I get that it's not the shiny new thing, but I don't understand people hating on it. Is this just junior devs who never learned it, or is there some new language out that I missed? (And please don't tell me Javascript....)
I mostly just don’t like some of the design decisions it made. I don’t like that lambdas can’t span multiple lines, I don’t like how slow loops are, I don’t like some functions seem to mutate lists and others don’t, and I am sure that there are other things I missed.
But it really comes down to the fact that my career hasn’t used it much. I occasionally have had to jump into it because of scripting stuff, and I even did teach it for a programming 101 course at a university, but I haven’t had a lot of exposure otherwise.
For scripting stuff for myself, I usually end up using Clojure with GraalVM (yes I know about babashka), or nowadays even just a static linked compiled language like Rust.
I don’t really understand why people think that compiled languages can’t be used for scripting (or at least task automation), honestly. Yes you have to add a compilation step. This involves maybe one extra step during development, but realistically not even that. With Rust I just do cargo run while developing, I don’t see how that’s harder than typing Python.
for x in [1,2,3]: print(x)
x sticks around! So you'll always have these random variables floating around, and hope you don't use the wrong one.
And to add insult to insult to injury, if you're using mypy for type checking you'll get a nice error if you try to reuse x with a different type:
for x in ['a', 'b', 'c']: print(x) << Incompatible types in assignment (expression has type "str", variable has type "int") [assignment]
And the types I can never trust. I've used all the tooling and you still get type errors in runtime. It's also ridiculously slow.
I would also like optional chaining (e.g. foo?.bar?.baz) and a million other features that other high level programming languages have.
"Insane feature" is a generous way of describing this behavior. I would say it is just a stupid bug that has managed to persist. Probably it is impossible to fix now because of https://xkcd.com/1172/
How typecheckers and linters should deal with this is a tricky question. There is how the language ought to work, and then there is how the language actually does in fact work, and unfortunately they are not the same.
Lucky for you, LLMs are pretty good at that these days.
>And the types I can never trust. I've used all the tooling and you still get type errors in runtime. It's also ridiculously slow.
IDE integrated mypy checking does this in the background as you type. As for errors, it all has to do with how much typing you actually use. You can set the IDE to throw warning based around any types or lack of type annotation.
Again, handholding.
msedit main.py && ./main.py
!!
!!
But indeed, pressing F5 solves that for both Rust and Python [1,2,3,4].filter(x => {
let z = x * 2;
let y = x * 3;
let a = x / 2;
return (z + x * a) % 27 == 2;
});
Obviously I know I could name this function and feed it in, but for one-off logic like this I feel a lambda is descriptive enough and I like that it can be done in-place.You're free to disagree, but I think there's a reason that most languages do allow multi-line lambdas now, even Java.
FWIW, you'd also have the benefit of being able to unit test your logic.
I have done a lot of Haskell and F#, and I'm very familiar with the concept of "lifting", and yeah being able to individually test the components is nice, but even within Haskell it's not too uncommon to use a lambda if the logic doesn't really need to be reused or is only a couple lines.
If you have a huge function, or you think there's any chance of the logic being reused, of course don't use a lambda, use a named function. I'm just saying that sometimes stuff that has 2-4 lines is still not worthy of having a name.
I have seen people do this in JavaScript quite often, but I always assumed there was some kind of underlying performance benefit that I didn't know about.
As I think about it I guess it makes sense if you're passing a function to a function and you just want it to be concise. I could imagine using something like that off the top of my head, but then pulling it apart and giving it a name the moment I had to troubleshoot it. Which is how I currently use nested comprehensions, just blurt them out in the moment but refactor at the first sign of trouble.
I think maybe I just have trouble seeing some of the braces and stuff, and it's easier for me to reason about if it's named. I guess that's why we have 32 flavors.
Thanks for answering me honestly I really do appreciate it, even if my tone came off as dismissive. Sometimes I don't realize how I sound until after I read it back.
> I have seen people do this in JavaScript quite often, but I always assumed there was some kind of underlying performance benefit that I didn't know about.
I don't think so, at least I haven't heard of it if there is.
I tend to have a rule of thumb of "if it's more than 6-7 lines, give it a name". That's not a strict rule, but it's something I try and force myself to do.
Like in Python, most lambdas can be done in one line, but that also kind of gets into a separate bit of gross logic, because you might try and cram as much into an expression as possible.
Like, in my example, it could be written like this:
[1,2,3,4].filter(x =>((x * 2) + x * (x/2)) % 27 == 2);
But now I have one giant-ass expression because I put it all into one line. Now where previously I had two extra names for the variables, I have the ad-hoc logic shoved in there because I wanted to squeeze it into a lambda.At it's core, I think it's fair to say Python is about forcing the user into formatting their code in a readable way. It's gotten away from it over the years for practicality reasons, and to increase adoption by people who disagree on which ways are more readable.
Sometimes I wish they would take nested comprehensions away from me, I am too lazy to avoid them in the heat of the moment, and I get a thrill out of making it work, even though I know they're disgusting.
Is it "better" than a named function? No, of course, they work mostly the same. But we are not talking about better or not. We are talking about syntax just for the sake for syntax, because some people prefer to write code in a way you don't necessarily care about.
I always thought the appeal of functional programming was more about test-ability and concurrency, it never occurred to me that people actually preferred the syntax.
Different strokes and whatnot, not everyone likes functional programming and of course there are valid enough criticisms against it, but I've always appreciated how terse and simple it feels compared to imperative stuff.
Even with regards to testability, if your function is pure and not mucking with IO or something, then even using a multi line lambda shouldn't affect that much. You would test function calling it.
Keep in mind, Haskell doesn't really have "loops" like you'd get in Python; in Python you might not necessarily need the lambda because you might do your one-off logic inside a for loop or something. In Haskell you have map and filter and reduce and recursion, that's basically it.
weeell, you can still do stuff like this in Haskell:
import Data.Vector.Mutable
...
let n = length vec
forM_ [0 .. n-1] $ \i -> do
next <- if i < n-1 then read vec (i+1) else pure 0
modify vec (+ next) i -- increase this value by next value
it's just in many cases the functional machinery is so accessible that you don't need to reach for for-loops.At a previous job I did manage to put a chip in that when I demonstrated replacing one of our Java services with a Python implementation. It was a fraction of the code, and achieved much better latency and throughput. Obviously not every Python program is going to do that. But my point isn't that Python is better, it's that these kinds of things are never so cut-and-dried. Many non-trivial Python programs are just thin shells around a large core of very well-optimized and battle-tested C/C++/Rust code. And Java, for its part, can also accumulate a lot of dynamic language-style performance losses to pointer chasing and run-time type lookups (and GC churn) if you're not careful about how you use generics. As always, the devil's in the details. It's also less able to pull the "actually I'm a C++" trick because using a compacting garbage collector makes it difficult to interop with native code without paying a hefty marshaling tax.
Still I believe Java is a better application language. Python is a better scripting language (replacement for Bash). Small apps tend to be easier on Python, but large apps are way easier on Java, both for syntax (types) and ecosystem (libs).
Seen plenty of coding horrors in both ecosystems...
class IWantToBeABean:
def init(self, arg1: int, arg2: str, arg4: str) -> None:
self._field1: int = arg1
self._field2: str = arg2
self._field3: str = arg3
def get_field1(self) -> int:
return self._field1
def set_field1(self, value: int) -> None:
self._field1 = value
def get_field2(self) -> str:
return self._field2
def set_field2(self, value: str) -> None:
self._field2 = value
def get_field3(self) -> str:
return self._field3
def set_field3(self, value: str) -> None:
self._field3 = value
when it could have just been: @dataclass
class IDontWantToBeABean:
field1: int
field2: str
field3: str
The worse case for Python is when you get people doing the oldschool Python thing of acting like dynamic and duck typing means it's OK to be a type anarchist. Scikit-learn's a good one to put on blast here, with the way that the type and structure of various functions' return values, or even the type and structure of data they can handle, can vary quite a bit depending on the function's arguments. And often in ways that are not clearly documented. Sometimes the rules even change without fanfare on minor version upgrades.The reason why large Python codebases are particularly scary isn't necessarily the size itself. It's that for a codebase to even get that large in the first place it's very likely to have been around so long that the probability of it having had at least one major contributor who likes to do cute tricks like this is close to 1. And I'd take overly verbose like the Java example above over that kind of thing any day.
get_field1(self) -> int:
"""
Gets Field1.
@rtype: int
@return: the field1
"""
self._field1
And use double underscores for private fields, because, you know, encapsulation.Seriously though, in Java you don't need get*() either. Typically you either:
1. use `record` types (since Java14, 2020), or
2. use Lombok that auto-generates them [1], or
3. use Kotlin `data class`, or
4. just raw field access. I don't buy the encapsulation thing if my code is not a library for wide internet to use.
I wouldn't call it a new language, for me it's just a syntactic sugar over Java, but for any problem you would google "how to do X in Java", not "how to do X in Kotlin".
But there you can do way simpler syntax, like:
0..100 meters with -45..45 deg within 3 seconds
Because "0..100 meters ..." is equivalent to "(0..100).meters(...)"(0..100) is a built-in IntRange type, that you can extend:
data class MyDistanceRange(val meters: ClosedRange<Double>)
val IntRange.meters: MyDistanceRange
get() = MyDistanceRange(first.toDouble()..last.toDouble())
andThe worst thing about Java is the average quality of Java programmer.The same could probably be said about Python. However I think that there are fewer Python programmers trying to write AbstractFactoryFactory than in Java. Java has a terrible culture of overly verbose, deep inheritance trees that make debugging and development worse, with worse performance.
Java programmers may not blog as much, and Java doesn't show up on Hacker News as much, but not being Extremely Online does not mean that it isn't extremely widely used by real people whose experiences are just as valid.
No, you could not. You could say it about maybe four others: PHP, C, C++, and C#.
No other languages have anywhere near the userbase size while being fairly quiet when it comes to online tech discussion.
I agree there are crusty old Java devs (as well as crusty old C, C++, PHP, etc. devs). In a decade or two, there will be crusty old TypeScript devs. It's just the nature of technology lifecycles and career paths. Some people get relatively tired of learning when they get older and just want to capitalize on what they already know for their remaining earning years.
Most of those rarely make to the top of HN, and other generalized forums. If anything, Java and Python are together at the popular kids table and we forget about the silent heros keeping the ship afloat.
The comment I was replying to seems to believe Java is a tiny backwater, which is anything but true.
Considering that as recently as 4 years ago I was working on a project where we still had a hard requirement to support running in Java 7, and this kind of thing was not considered unusual, I can't really disagree too strongly with that. Yes, that was still inside of Java 7's extended support period, so there was really nothing unusual or surprising about this, from a Java developer perspective. But that's kind of the point.
It's also not really a bad thing, considering what kinds of things run on Java. Mainframe developers have a similar thing going on, for a similar and similarly good reason.
I recall things like updating packages/code on the fly, recompiling fast paths on the fly. Maybe that's not necessary in a borg/kubernetes world where you can just restart things and have your load balancer infra take care, or just run slower code because compute isn't that expensive once you accelerate your CPU-heavy libraries, but cool anyways.
Edit more succinct
The Log4shell incident is the perfect demonstrator of what kind of people are in Java world.
To what? Java is still a very efficient, productive language. Updating a legacy codebase to use newer Java features would probably be good, but migrating to another language is unlikely to significantly move the needle in terms of runtime performance or developer velocity.
When you work with a codebase that doesn't need compilation, the development velocity is quite fast. You don't need to compile, you can prototype features on the fly, you can even write real time updates while the web server is running.
And standard compilation in Java is done in a VERY inefficient manner with groovy - you have groovy that gets compiled to bytecode, which then gets JIT compiled to native, which then actually runs the compilation of the source code to bytecode, and then you have a first startup latency as it gets JIT compiled to native.
All you really need to write any modern app is Python + C. Anything that needs to go fast, just encapsulate in a small C program, and launch that from Python.
In fact, a lot of the most interesting plt and compiler r&d going into real world applications is on the jvm (project loom, graal etc), and the features of modern Java (pattern matching, records, etc) make it a great choice for lots of projects that aren’t legacy enterprise apps.
I don’t think Java makes anyone unambitious, I think it’s that Java is taught in schools and unambitious people don’t feel the need to learn anything else, and they get jobs at unambitious corporations. It selection-biases towards unambitious people who don’t want to learn more than they have to.
Compare this to something like Clojure or Haskell or something, which isn’t routinely taught at schools and is not very employable. People who learn these languages generally seek out these things because they’re interested in it. This selection-biases towards intellectually ambitious people.
As a result, Java people can be insufferable for people like me.
The people who make Java have actually made the platform and language pretty ok in the last two decades, but I had to fight at a previous job to use NIO, which I think was introduced in Java 4, but none of my coworkers had really heard of it or used it because the regular Java blocking IO has been “good enough”.
Given fact that Lombok is still pretty much widely used, with its under the hood functionality of essentially hacking the AST, or the fact that annotation processors write out Java code to files, or the fact that you could be using a standard library like Log4j and have a massive vulnerability on your system because someone decided that it would be a good idea if log statements could execute code and nobody said anything otherwise, or the fact that Kotlin and Groovy were literally made to address inefficiencies in Java, amongst other things....
Yeah not really sure how you came to that conclusion.
Kotlin and Groovy did come and address problems with Java, you should use use them if your employer allows it. I'm just saying that Java 21 is actually kind of fun to write.
Yes, some of the libraries have been unsafe, but that's one example of the 30 years of Java.
I just feel like Java has improved in the last twenty years. It's the engineers that haven't.
I think one comment I saw here on HN that Java is better if written in Pythonic way. I agree completely with that stance.
but yeah within Java you've 'merchants of complexity' people who wanna do things in the most abstract way rather than the simple way.
btw Java can be as simple as Go.
And the slides are available here: [url-redacted]
I'm afraid that my humor isn't really reflected in the slides, but imagine everything here is said kind of snarkily.
Java can be mostly as nice as Go, the BlockingQueues and Virtual Threads can get you pretty far, though they're not quite as nice as Go channels because there's no real way to select across multiple BlockingQueues like you can with Go channels.
Overall though, I think Java 21 is actually not too bad. Shockingly, I even sometimes have fun writing it.
Should be fixed now. Sorry about that.
Other than being ageist, it’s wrong; or misattributed to Java. I work with Python every day, and what’s missing is static typing and IDEs that make use of it to greatly reduce the amount of code and context I have to store in my head. Python (a dynamically typed language obviously) is exhausting to maintain. But easy to write. Java/C#/whatever statically typed language with great IDE is easy to write and maintain by comparison.
Of course there are IDE for Python and dynamically typed languages, but everyone I’ve tried has fallen short compared to the best Java/c# IDEs.
Static vs dynamic used to be a huge flame war on the internet, but over the past few years I’ve encountered people who’ve never heard of it. This is it.
- Environment / dependency management
- Type safety
- Performance
As the author points out, these have largely been addressed now by uv, type hints, pydantic, FastAPI, etc.
Not really.
Environment/dependency management is/was never an actual problem. People act like you update a version and stuff breaks. Even then, venv existed for this reason, and you could specify version in the setup.py or requirements.txt.
Type safety should in theory be covered by unit tests. If you assign a variable to another one in a dynamic setting accidentally, like a string to a dict, then your functionality breaks. In reality though, its really not that hard to use different variable names. In my experience, the only time things start getting confusing is if you start using Java concepts with things like Factories and a lot of OOP and inheritance in Python. None of that stuff is really necessary, you can get by with dicts for like 90% of the data transfer. And in very type safe languages, you spend a lot of time designing the actual type system instead of just writing code that does stuff.
Performance is still slow, but language performance doesn't really matter - you can launch natively compiled code easily from Python. Numpy was built around this concept and is also 10 years old. You will never beat C (with explicit processor instructions for things like vector math) or CUDA code, but most of your code doesn't require this level of performance.
Backward compatibility, which I suppose is closely related to needing to use env, is also a pain. In my experience you can't go forward or backward in many cases. It's especially painful on projects that don't change very often. I'm sure that an active codebase can probably be kept updated from version to version, but if you've waited a bunch of versions, it seems painful.
But, I'm not sure I've given it a fair shake because I haven't needed to. It's use in AI does make it an attractive option, now more than ever.
- statically typed languages got better, reducing the relative benefits of dynamic typing. people realized they didn't really hate static types, they hated the way [insert 90s-00s enterprise language here] did static types
- the GIL became more and more painful as computers got more cores
- it used to be a language for passionate hackers, like a latter-day lisp (cf that old pg essay). "written in python" used to be a signal of quality and craftsmanship. now it's the most commonly taught beginner language; millions of bootcamp devs put it in their CV. average skill level plunged.
- PSF was asleep at the wheel on the packaging / tooling problems for years. pip/venv are dinosaurs compared to cargo, nix, or even npm.
Seems more like it's fallen out of favor with senior devs who have moved to Go/Rust.
Python compares fairly well to Bash or JavaScript or whatever, right? (Maybe JavaScript is better, I don’t know anything about it).
JavaScript has much more intuitive async syntax, which was actually borrowed from a Python framework.
For whatever reasons, the Python folks decided not to build on what they had, and reinvents things from scratch.
It seems like two of the main entries under “Python stuff” are “working with people who only know Python” and “AI/ML because of available packages.”
What are some others?
- Interpreter version management: uv can handle separate python versions per project automatically. No need for pyenv/asdf/mise.
- Bootstrapping: you only need the uv binary, and it can handle any python installation, so you don't need to install python to install a management program.
- Environment management: uv will transparently create and update the environment. You don't need to source the venv either, you use `uv run...` instead of python directly.
Overall, it makes common Python project management tasks simple and transparent. It also follows standards (like project definition metadata and lock files), which poetry often doesn't.
I did just update the dependencies of some of my projects. I could see how that could be faster. I don't do that often enough for me to care about it, though. `poetry run pytest` is the slowest thing I have, and I'm confident most of that slowness is in my direct control already.
I'm intrigued on the lock file point. I thought that was literally one of the main reasons to use something like poetry, in the first place? Does uv have a better lock file mechanism?
I'm very fortunate that my python projects are all relatively small, so maybe that colors things a bit. Certainly looks like something that would have swayed me to uv at the start, but as things are, I think I mainly just wish there was a more standard/accepted work flow for build and release.
It doesn’t ship with a first party package manager so you got the community trying to fill this gap. Use any other language with good tooling like golang or rust and it is a breath of fresh air.
Python used as an actual PL is a footgun because it’s dynamic scripted. (Don’t tell me about using tools X, Y, Z, mypy, …) You essentially become the compiler checking types, solving basic syntax errors when uncommon paths are executed.
A programming language that’s only good for < 100 line scripts is not a good choice.
I honestly wish python were in a better state. I switched to rust.
What's wrong with using tools that improve on common issues? I don't think I'd use Python without them, but ruff and pyright make Python a very productive and reliable language if you're willing to fully buy into the static analysis.
What a bunch of crap. It's so trivial to show very popular and useful programs written in Python that far exceed this number I'm not even going to do the work.
What a lazy criticism.
As python projects grow and grow, you need to do lots of support work for testing and even syntactic correctness. This is automatic in compiled languages where a class of issues is caught early as compile errors, not runtime errors.
Personally I prefer to move more errors to compile time as much as possible. Dynamic languages are really powerful in what you can do at runtime, but that runtime flexibility trades off with compile time verification.
Of course, every project can be written in any language, with enough effort. The existence of large and successful python projects says nothing about the developer experience, developer efficiency, or fragility of the code.
In hindsight, I should have just left it alone and not replied which is what I usually do. But Python's popularity isn't an aberration. It's tradeoffs make sense for a lot of people and projects. The low effort bad faith swipes at it from subsections of the HN community got me a bit riled today and I felt I had to say something. My apologies for a less than constructive critique of your comment.
Best.
> In Comments
> Be kind. Don't be snarky. Converse curiously; don't cross-examine. Edit out swipes.
> Comments should get more thoughtful and substantive, not less, as a topic gets more divisive.
> When disagreeing, please reply to the argument instead of calling names. "That is idiotic; 1 + 1 is 2, not 3" can be shortened to "1 + 1 is 2, not 3."
> Please don't fulminate. Please don't sneer, including at the rest of the community.
> Please respond to the strongest plausible interpretation of what someone says, not a weaker one that's easier to criticize. Assume good faith.
Today the IDEs got much better, but I still can’t see the significant whitespace as anything than downside. With brackets I can indent, but also have automatic indenting succeed every single time.
I just have a line in my Justfile that does this. Probably would be better to format on save but I use different editors and haven’t gotten around to it.
Still doesn’t fix the creeping doubts about everything in a language conceived by people who made that whitespace call, but it removes the specific pain point.
If you copy and paste some Python code and it isn't indented properly, it breaks the python program. It's the stupidest thing I've seen in any language, including javascript (which isn't as stupid as many claim it is).
It's still reigning champion of data science, and of course it has a huge number of uses and users still around, but it's not really cool or in vogue outside of those circles.
And what is lightweight scripting? Isn't scripting by definition lightweight?
But Python overall is still very popular outside the Data Science-circles. Not sure where this claim is coming from.
This is a wild take. You're never going to get a fully accurate measurement but every source I've seen[0][1][2] puts Python as the most common programming language by a country mile.
If it doesn't seem "cool" or "in vogue", that's because everyone is already using it.
[0] https://www.tiobe.com/tiobe-index/ [1] https://pypl.github.io/PYPL.html#google_vignette [2] https://www.pluralsight.com/resources/blog/upskilling/top-pr...
The famous answer.... _it depends_.
Plus there has been a rising sentiment against dynamic typing by masochists over the last decade or so.
> by masochists
Hey! The masochism pays dividends. I can't do anything with duck typing that I can't also do with `dyn Trait` abuse :)But then uv came along. It's not just another poetry or pipenv or whatever. It works well and it has uvx and `uv tool install` and other nice things.
Previously when I saw that something was written in Python I'd curse under my breath because I don't have the mental energy to make a virtual environment and source its shell script and remember where I put it just to try something out. On arch Linux I can't just pip install something without going through some crazy setup that I used to have patience for but as I get older it's been better to just curse that snake language altogether and spend time doing something more chill like writing rust.
But now I can just type "uvx" and it works. I'm probably not going to start writing python again any time soon, but at least now I have less reason to be disappointed with people who themselves choose to write Python.
print "xyz"
now and then. It's not a big deal, but it reminds me of struggling with pip->pip3 and many other things for a long time years ago.
Tbf at that point Django was still pretty shaky with 3 and basically none of the 3rd party Django libraries supported it at all, plus I was using Google AppEngine which at the time was tightly coupled to the 2.7 runtime. But really I was just parroting the Slashdot hivemind which was 100% convinced the transition was the new Perl 6 and would kill Python, and that Python.org was dishonestly teaching newbies who didn't know better a dead language and worthless skill when they changed the default.
Fortunately for him he ignored me and most of the big Django libraries were ported like a year later at which point I had to switch anyway to get updates. Fully agreed that in 2025 it's pretty much irrelevant, and honestly despite some legitimate pain the transition was much more successful than the cynics assumed it would be at the time.
Everyone will mention uv/pyenv/poetry/conda/virtualenvs, so fine, let's pretend it's not a problem that you tried each of those in desperation and they are all household names. Suppose the packaging wars actually ended and everyone uses what you use without you needing to tell them, and suppose further that every pypa problem isn't blaming debian maintainers for obvious regressions. Pypi will still yank[0] packages at the source to perhaps randomly break deterministic behaviour, smashing anything in the blast radius rather than only clients using a --strict flag or something. You can pin your own dependencies but who knows what they will pin (nothing, or the wrong stuff, or the wrong way probably!) or what they will yank. Now for repeatability you need to host a mirror for everything you use- which is fine for corporate but a nonstarter for the novice and annoying for FOSS projects.
If you have zero dependencies or a slowly changing environment that is receiving constant care and feeding, you'll probably never notice how bad things are. If you put down most projects for a month though and pick them back up, perhaps with a different environment, machine, or slightly different python version it's broken, bitrotted, and needs serious attention to be rehabilitated.
People might argue.. that's just software dev. Nope. I say it with lots of love and gratitude for the efforts of the community.. but most langs/ecosystems would never tolerate this level of instability. One has to eventually just admit that working, portable, and actually repeatable environments with python basically just require docker. Can we talk about how "--break-system-packages" is hilarious? After you retreat to docker you can type this a few times a day, push the container up, pull it down months/years later, and then realize that literally the only way to get a stable working environment is to request a broken one. QED
Of course then crypto bros happened and the rest is history.