Back to News
Advertisement
Advertisement

⚡ Community Insights

Discussion Sentiment

63% Positive

Analyzed from 7684 words in the discussion.

Trending Topics

#type#python#types#code#language#none#don#typed#typing#languages

Discussion (190 Comments)Read Original on HackerNews

dsign1 day ago
If you are going to be super-strict with type-checking, wouldn’t it be best to switch to a statically typed language and get the performance gains as well?
pmontra1 day ago
Hallelujah, that's always been my position. To the static typing folks: leave my dynamically typed languages alone and go coding with something that really suit your needs. If the answer is that Python, Ruby, JS, whatever are really much more pleasant to code with, my reply is that they are so precisely because we don't have to type type definitions. Tradeoffs.
cogman101 day ago
It's not an all or nothing thing.

I think types are particularly valuable for libraries. A library author using copious types really helps the downstream user to know "Ok, this function returns a dict(Foo, Bar)". But after that, it's a matter of preference if you want to add those types to your own code or not.

Having the types in the libraries makes it a lot easier for your tools/IDEs to give good suggestions and catch bugs that you might otherwise miss.

eloisius1 day ago
Yes, where would I be without the _RelationshipBackPopulatesArgument type of

        sqlalchemy.orm.relationship(argument: _RelationshipArgumentType[Any] | None = None, secondary: _RelationshipSecondaryArgument | None = None, *, uselist: bool | None = None, collection_class: Type[Collection[Any]] | Callable[[], Collection[Any]] | None = None, primaryjoin: _RelationshipJoinConditionArgument | None = None, secondaryjoin: _RelationshipJoinConditionArgument | None = None, back_populates: _RelationshipBackPopulatesArgument | None = None, order_by: _ORMOrderByArgument = False, backref: ORMBackrefArgument | None = None, overlaps: str | None = None, post_update: bool = False, cascade: str = 'save-update, merge', viewonly: bool = False, init: _NoArg | bool = _NoArg.NO_ARG, repr: _NoArg | bool = _NoArg.NO_ARG, default: _NoArg | _T = _NoArg.NO_ARG, default_factory: _NoArg | Callable[[], _T] = _NoArg.NO_ARG, compare: _NoArg | bool = _NoArg.NO_ARG, kw_only: _NoArg | bool = _NoArg.NO_ARG, hash: _NoArg | bool | None = _NoArg.NO_ARG, lazy: _LazyLoadArgumentType = 'select', passive_deletes: Literal['all'] | bool = False, passive_updates: bool = True, active_history: bool = False, enable_typechecks: bool = True, foreign_keys: _ORMColCollectionArgument | None = None, remote_side: _ORMColCollectionArgument | None = None, join_depth: int | None = None, comparator_factory: Type[RelationshipProperty.Comparator[Any]] | None = None, single_parent: bool = False, innerjoin: bool = False, distinct_target_key: bool | None = None, load_on_pending: bool = False, query_class: Type[Query[Any]] | None = None, info: _InfoType | None = None, omit_join: Literal[None, False] = None, sync_backref: bool | None = None, dataclass_metadata: _NoArg | Mapping[Any, Any] | None = _NoArg.NO_ARG, \*kw: Any) → _RelationshipDeclared[Any]*
mort96about 15 hours ago
I do think it is somewhat of an all or nothing thing. I can write dynamic languages, sure; I prefer having static types, but I have written a lot of dynamically typed code. However if I'm working in an editor with LSP integration, the experience is much worse when some things are missing types.

As an example, I may have a variable with types:

    const something = somelibrary.getSomething();
and I can type `something.` and see methods and properties. However, if my own code doesn't use types consistently, it's so easy to lose type info. For example:

    const something = someWrapper(someLibrary.getSomething())
or:

    function doSomethingWith(something) {
        ...
    }
    doSomethingWith(someLibrary.getSomething()
or any number of other patterns which accidentally strips type info from the variable if you don't use types everywhere.

I would much rather have a language where the compiler complains if some variable doesn't have a static type, than a language where I can accidentally leave something untyped. I don't understand which case I would want a variable or function to not have associated static type information.

shevy-java1 day ago
This is even worse because you attempt to try to sell why types SOMETIMES make sense. But you aim with this for a language that did not have nor need types to begin with. People don't seem to understand that this is an issue.

The library-situation is really not different from having types everywhere, and some people will do that too.

> catch bugs that you might otherwise miss.

People repeat this a lot. In about 22 years of writing ruby code, I have never ran into a situation once where I would have caught a bug through types. I don't understand why people keep on repeating this. Repetition does not make it anymore true.

Think in the opposite way: if types would have been necessary to begin with, why would ruby have been successful back in 2006? It was successful without types already. And types were never needed - they came because some people THINK they are needed. This is the biggest problem - the thinking part. They think they are right and all who do not use types, must be wrong and very foolish people.

tasuki1 day ago
> It's not an all or nothing thing.

It kind of is? All the partial-typing systems are too complex and usually broken in various ways. Compare to eg Elm or Gleam which are typed and super simple.

Tade01 day ago
Personally I like having my TypeScript cake and eating it.

I also truly believe those who design type systems would benefit from taking a look what kind of code people programming in dynamically-typed languages produce.

anamexis1 day ago
I do too, but I feel like TypeScript stands alone as an unusually effective and pleasant to use bolted-on type system. I've not seen any other approach come close. (My sample size is Python, Ruby and Elixir)
zokier1 day ago
I find it funnysad that python people coined the phrase duck typing and then ended up designing what they have now. Meanwhile TS manages to embody duck typing far better even though coming from very different background.
throwaway20371 day ago
Does Python needs its own TypeScript moment? Many times, while writing Python and deeply frustrated with its weak(er) type system, I have dreamed of something like TypeScript or VB/VBA from the early 2000s (where the type system was surprisingly strict!). However, there are so many Python libraries written in pure C, it is way harder to create a TypeScript equivalent.
joha42701 day ago
Could you point me towards the kind of code people programming in dynamically-typed languages produce?

I have lived in statically typed languages almost all of my life, and even when I don't, I pretend I do, just without having a typechecker. So I'm very curious about what I'm missing.

junon1 day ago
I hate TS's tooling with a burning, deep passion. But its type system is actually pretty incredible for what it is.

There are times that I yearn for TS's ability to do duck type reasoning in e.g. Rust (despite that not being feasible) when working with very large data types.

chocrates1 day ago
My engineers write better code when we enforce types.

It's easier to do this then retrain everyone on Go and rewrite all our code.

New stuff is often in Go now, but prototyping quickly in Python and then enforcing types when we have to get it ready for production has been working decently

throwawayffffas1 day ago
Totally agree. I hear a lot of rust makes it hard to write incorrect programs. In my experience it makes it hard to write programs in general.
zelphirkalt1 day ago
So far I have been avoiding Pydantic as a huge-ass dependency. Instead I am relying on standard type annotations, lots of typed dicts and at service/program boundaries use a jsonschema. I like being able to specify the type of most functions, and get some hints, completions and so on, but I don't want to _have to_ specify every darn type. I also don't want to write a class for everything. Typing dicts is good and usually sufficient. If I wanted to write types for everything, then I could also just write Java or Rust or similar.

Unfortunately, I think the kingdom of nouns faction has long invaded the Python world and I see more and more companies demanding Pydantic and similar things. They are dragging us all the way to Java land, it seems.

strogonoffabout 13 hours ago
I tend to get triggered when TypeScript is painted as “JS with type hints”. Coming from Python background, TS and Python with type hints are just so different.

With Python I can’t see myself type-annotating everything (or bringing in pydantic anymore for that matter, it is indeed becoming a blight), but with TypeScript my process is turned on its head: I find it natural and easy to start writing with types and have everything fully typed, and I find the fact that it simply won’t compile if anything is off (compared to Python where it’s more like “one of my N type checkers/linters failed, oh well it still runs though) a useful constraint that gives peace of mind.

Hizonner1 day ago
The only reason I gave up resisting and started writing any significant code in Python at all was that it got some kind of type system, and thus became less unpleasant to code with.

"Pleasant to code with" does not describe getting "AttributeError: 'NoneType' object has no attribute 'foo'" 25 levels deep in a stack trace already obfuscated by dynamic object-oriented nonsense. In production, because it's an unusual case and testing missed it. Not that test cases aren't way more work than types anyway.

thr1owaway96211 day ago
I started using types with Python in 2018-ish, and I never looked back.

I am not that good a programmer, so maybe I am wrong, but I just like being able to tell what the data is that's moving through the system. Typed function signatures, a little shift+k here and there, a warning that I am trying to add int and a string. I don't see what's the harm in having that?

At the end of the day, if you don't want to use Python with types -- do not. Unless somebody at work is forcing you, and it feels like putting lipstick on a pig (especially with something like numpy that doesn't easily support types)? Then condolences.

nonethewiser1 day ago
But why does your appreciate of type systesm not lead you to something like Typescript? Which is a lot more robust? Or Rust? C#?

I guess my speculation is that not every language is good at everything. Sure you might want a better type system with Rust. But for data science?

kibwen1 day ago
> To the static typing folks: leave my dynamically typed languages alone

Surely you understand that the push to add types to dynamically-typed languages comes from dynamic-typing folks, not from static-typing folks. People who are deeply into static typing have little incentive to consider e.g. Python, whose support for types is relatively weak, loosely-defined, and rarely-enforced compared to the statically-typed languages that exist today.

kh_hk1 day ago
Doesn't it come from folks that are forced to work with dynamically-typed languages but can't be arsed to understand them?
wolvesechoesabout 17 hours ago
> People who are deeply into static typing have little incentive

Except when their boss tell them to use Python, or they rely on one of Python libraries that their pet language couldn't provide via its powerful type system.

bborud1 day ago
What specifically makes them more pleasant? (not a rhetorical question, I want to know what's important to you)
pmontra1 day ago
Historically (80s/90s) I started using Perl because I didn't have to write all those malloc and free I spent years writing in C and I could perform string operations much more easily. Then in the mid 90s because of that wonderful CGI.pm Perl module. But the plus of all those languages, and Java, was managed memory. Then in the mid 2000 I learned Rails, and after Rails I learned Ruby. It was like Perl but much easier to understand and again no types to type. Basically what I did in Java but in a fraction of the time and in a fraction of the lines of code. Then a customer asked me to work with Python on a Django app so I learned Python. It looks like a Ruby designed by Klingons but it's OKish.

All those bugs I constantly read about, they don't happen very often and are a good tradeoff. Maybe Rails and by Django are shielding me from some bug scenarios.

adrian_babout 7 hours ago
> because we don't have to type type definitions

Typing "type definitions" makes you type less, not more, because you type the definition only once, instead of writing many tests wherever values of that type are used.

In a decent programming language, one would frequently avoid the need to declare the type of a variable, whenever the type can be deduced from the value used to initialize the variable.

Having types in a language has 2 purposes, one is to enable the compiler to check at compile time or at run type that all the subsequent uses of an identifier after its first occurrence are consistent.

I cannot imagine which are the benefits for the programmers who are against this rule, i.e. who want to reuse the same identifier for multiple purposes in the same scope (N.B. reusing an identifier in the same scope has nothing to do with data types that are disjoint unions or virtual types, which can be used in any type-enforcing language, or with reusing the same identifier in different scopes).

The second purpose of data types is that when the type of a variable or parameter is known at compile-time, that allows more efficient implementations, which are especially important for aggregated data, e.g. arrays.

Again, I also do not understand why anyone would want to have inefficient data representations, to avoid the need of data types.

There has been some argument that when a language does not use data types you might avoid having to rewrite some library functions if you want to change the parameter types at invocation. However this is a problem that has been better solved for more than a half of century, by providing various means to write generic functions that can be specialized at compile-time or by using disjoint union types or virtual types, for using the same function for many different data types, while still ensuring that other data types, whose use would be erroneous, are rejected.

A language without data types saves writing effort only when the programmer omits the run-type checks for correct values, which would be needed to avoid bugs when such checks are not done automatically by the compiler.

I agree that several very popular programming languages with type checking, including C and C++, are very poor examples about how a type system should be implemented, because they require the writing of a great amount of superfluous boilerplate that is completely unnecessary (e.g. writing headers with function declarations instead of extracting automatically the interfaces of a package a.k.a. module or writing explicit type names in a lot of places where they can be deduced automatically from the context).

Such languages are strawmen in a discussion about whether a language must enforce type checking or not.

bitwizeabout 16 hours ago
God, I hope you never touch a production code base with real users. Strong, static typing has won. It is a must for serious software development. The time and money saved in stupid errors that are caught and avoided before the software even runs is enormous. For internal tools and one-off scripts, sure, go nuts with dynamic stuff, but there is no reason to use a dynamic language for code paths that actually make the business money. If you cannot specify precisely the type of every piece of data you touch and all the operations that can be performed on it, you're not doing software engineering, you're spitballing. And if you want to spitball, LLMs are here and they're great. You can type in a very loose description indeed and get properly typed Rust out the other end.
shevy-java1 day ago
I am totally with you and I am glad I am not the only one who is totally against those type-addictions leaking into languages that did not need them in the first place.

Types in ruby are even worse than in python, because the type systems in use really make ruby turn very ugly. In python it is not as much as a huge problem with regards to syntax, as python has a stricter syntax (e. g. mandating foo.bar() whereas in ruby you can typically omit the (), among other syntax sugar examples).

We need to keep the type people out of those languages.

Many years ago, on IRC, on #haskell, they said they don't want everyone to use Haskell. Back then I did not understand it. After the type-addicted people emerged out of nowhere, I now begin to understand why Haskell is so snobbish. If you let every idea float, you end up ruining languages - and then those who wanted this, will retire and move away too. Ultimate damage factor caused as outcome here.

chpatrickabout 23 hours ago
Nothing can beat the Python numpy/ML ecosystem. There's a lot of value in just being able to run a Python script as well without any compilation step. The typing isn't perfect right now but it's usable.

For vectorizable problems there also won't be huge performance gains from switching to a compiled language because all the hard stuff is already done in highly optimized native code. The only time it really makes a difference is if you have to write a custom for loop or traversal.

ocamoss1 day ago
Running more type checkers isn't really about strictness. The main benefit to library maintainers is to make sure that their APIs are compatible with whatever tools their users run.

This wouldn't really be an issue for most other languages, but Python's typing ecosystem is uniquely fragmented, with only partial standardization between several popular tools.

fg1371 day ago
Hmm... that doesn't answer the question?
locknitpicker1 day ago
> Hmm... that doesn't answer the question?

GP's point is obvious: performance is immaterial to the discussion. Static code analysis is about preventing bugs. Therefore OP fails to make any sort of point, as it's a straw man argument.

tptacek1 day ago
No? One has nothing to do with the other.

I think those of us who work in compiled languages are just snooty about them.

I'm a compiled language snoot, and happen to be working over the past couple days in typed Python for the first time. It's kind of nice. I like it. It's a huge improvement for me over ordinary Python/Ruby/Javascript; it materially improves the experience of working in the language.

tasuki1 day ago
> happen to be working over the past couple days in typed Python for the first time. It's kind of nice. I like it.

I like me a good type system and have always hated about everything about types in Python. What do you find nice and like about it?

(My experience with Python: all the type checkers are broken, there are false positives and false negatives everywhere. The LSPs are likewise broken, I have not found one that knew the types at least somewhat reliably...)

tptacek1 day ago
Lack of typing is my biggest problem with Python, Ruby, and ES6 Javascript; I have to write everything twice, once to do the stuff I want, and once to double check that it's actually doing stuff, because a single typo blow the program up despite it parsing fine.

Python typing is easy to dip in and out of. It handles None nicely; not as nicely as a true Optional, but enough for daily driving. The annotations are readable and simple. What more could I ask for, without asking for an entirely different language? Python typing catches a lot of bugs I'd otherwise have to tediously unit-test for.

The only thing I don't like about it is that it feels like it relies a lot on importing stuff from the swamp of the Python stdlib.

Animats1 day ago
The modern approach seems to be to require full typing on items seen from outside the function or object. Within functions, have the compiler infer as much as it can. Newer languages (Go, Rust) seem to be converging on this approach.

Function parameters need type info as guidance for people and LLMs calling the function. Even though cross-function type inference is technically possible, it's too confusing. Long-distance inference failures tend to generate poor messages.

Within a function, if you have typed parameters, the type inference engine has a local starting point and a good chance of success on most local variables.

Unchecked advisory typing in Python was a terrible idea. All the work of writing type declarations with none of the benefits.

Qem1 day ago
> If you are going to be super-strict with type-checking, wouldn’t it be best to switch to a statically typed language and get the performance gains as well?

You can use type-checking to get better performance already, without leaving Python. See https://blog.glyph.im/2022/04/you-should-compile-your-python...

SatvikBeri1 day ago
What statically typed language would you suggest for machine learning and large data pipelines? I don't love Python, but it has by far the largest ecosystem.
FacelessJimabout 10 hours ago
You could take a stab at Julia.

It’s still dynamic in nature. But you can tune how much staticity you want. The spectrum goes from Python to C in terms of staticity. And with tools like JETLS.jl maturing you get a lot of the benefits static analysis.

The data pipeline ecosystem is starting to rival that of R and Python. The fact that you can just use Julia functions while keeping the performance allows you to avoid those weird vectorization gymnastics. The ML ecosystem is also in a great state. JUMP.jl, Touring.jl, the whole SciML ecosystem, autodiffing and gpu computing are all close to best in class in terms of quality. The NN side of ML is a a bit weaker, but just for lack of developer time investment into that side of the ecosystem.

SatvikBeriabout 2 hours ago
I use Julia! I like it a lot, and add type parameters to all application code. But JET.jl does not feel anywhere close to the assurances I can get from a statically typed language (yet)
throwaway20371 day ago
It is brutal. I can say with first hand experience: The APIs for Pandas and NumPy are awful and insanely dynamic. As a result, it is frequently difficult to know what is allowed with calling a method. It is exhausting. Since many methods are "hyper-dynamic", many of the error messages are unhelpful.
bborud1 day ago
Well, that's the curse of machine learning: since everyone uses Python you have to deal with Python. Even though Python isn't very nice when things start to get serious and you don't want to spend your time fiddling with noise just to make something work at scale.

I'd wish the ML/AI/LLM crowd would see that it is in their interest to get better developer ergonomics at scale. (I don't want to have to turn to C++)

nijave1 day ago
Anything performance sensitive ends up being an extension in compiled code anyway

Python is mostly just glue

srean1 day ago
You could try Cython and Lush. An ML dialect for ML would have been nice, but doesn't exist.
thr1owaway96211 day ago
Cython is a niche language for writing perf-critical bits inside your Python codebase. It's like C for people who don't want to learn C. At least that's how I treated it, when I had to write some stuff to make some numpy ops faster.

Cython is not in any real sense a replacement for a modern data/ml stack.

pdpi1 day ago
As funny as it would be, ML isn't really a great fit for ML, I don't think.
mountainriver1 day ago
ML is basically the one use case for Python anymore.

And that even shrinks by the day

sideway1 day ago
How so?
locknitpicker1 day ago
> What statically typed language would you suggest for machine learning and large data pipelines? I don't love Python, but it has by far the largest ecosystem.

Pay no attention to OP. It's nonsensical to even suggest you should migrate away from a whole tech stack just because you want to run static code analysis, specially when the argument is based on having too many static analysis tools to chose from. Utter nonsense.

fastasucanabout 7 hours ago
That depends on your needs and goals. Super strict type checking might not be the most important feature.

Its like saying "if you want a car that is a bit sporty, wouldn't it be best to buy a Lamborghini??

dsignabout 7 hours ago
Yes, but using five type checkers on Python is the equivalent of buying a Nissan Leaf and trying to turn it into a Lamborghini by adding an internal combustion engine which is only supposed to produce a nice roar but no thrust.
MeetingsBrowser1 day ago
strict type checking is an incredibly useful tool for cases when you really want to make sure your code is correct and behaving as expected (one of many tools).

There are lots of people who like python and want to use it for things that where incorrect code has serious consequences. Type checking is helpful in these contexts.

Type checking remains optional for the masses and is not practical in many cases. Still, pushing away people who want to use all available tools for writing correct python only hurts the community.

throwaway20371 day ago
Shhh! You're not supposed to say that part out loud.
vadansky1 day ago
Personally because I'm making a blender add on that only uses python, and it's at the complexity where having types catches a ton of bugs easily.
marcosdumay1 day ago
The goal is to be strict, with explicit exceptions.

You don't use a static language because you want the exceptions, but the type checking can still statically validate most of your code.

ethagnawl1 day ago
Yeah, I can't say I really get the appeal of gradual typing. It's commented/documented code at best and outright lies at worst. Sure, you can build tooling around it and improve your DX a bit but isn't it always a house of cards?
falcojr1 day ago
If you add one of these type checkers into your CI or a pre-commit hook, it provides the same guarantees you get from a compiler along with the same tooling benefits. It gives you the option of using the structure when you need it, but not being forced to use it when you want to take advantage of some of the more dynamic features of the language.
chuckadams1 day ago
But none of the runtime benefits of having static types in the compiler, since the runtime still can't trust the types. Still, half a loaf is better than none.
ocamoss1 day ago
> commented/documented code at best

Machine-checked documentation is always valuable, IMO

UltraSane1 day ago
That is why I'm using C# and Rust more now than Python. You get far better RoI on types. and they are so much faster and can use all cores so much more easily.
lijok1 day ago
ML

Data tooling

Talent pool

Libraries for customers

Brownfield codebases

Academics

I can keep going…

bborud1 day ago
Python has other, bigger problems that make it a constant headache. One of them being the dismissive attitude towards any and all of problems that come from versioning, dependencies and quirks that make it challenging to have robustness.

Criticisms are typically dismissed by suggesting heaping yet another "solution" onto the growing pile of "solutions" that you have to drag around with you. That people have to learn. That you have to install tooling for. That has to be vetted. That has to become part of the toolbox to get even seemingly simple things done. This attitude is a big part of the reason that I strongly advise people against using Python in production. On top of all the problems presented in a real-world setting.

Almost all of the time, people who are fond of Python are more interested in defending python, disparaging me, downvoting me etc that listen to why I make that recommendation.

(I get it. People like Python. What I think of Python as a language is irrelevant. In fact I don't have that much against it. But I do have a lot against it in a setting where you need reliability and repeatability)

I have spent the last month of my life building a system that can run Python tooling reliably in a business critical application. I knew this was going to be a pretty big job when I started, but for every problem I solve, a bunch of new problems arise. I am starting to see light at the end of the tunnel but it hasn't exactly been smooth sailing. I'm almost there for a first version, but there are a bunch of problems still to solve. Mostly because I care about developer ergonomics and that things should "just work". One important goal is that my solution shouldn't impose any significant cognitive burden on people who use it. That's really hard.

(I don't think the solution will be open source since my contract wouldn't allow for it. But I'll make the case at some point for why it should be open sourced)

And yes. There are statically typed languages available today that have decent tooling that provides superior developer ergonomics. I can understand that people don't want to learn new languages, but if you have the capacity to do so I would recommend trying to move on if the code you write has to run outside your own workstation. If an old fart like me can learn and adopt new languages, so can you.

chuckadams1 day ago
Python the language is pretty nice. It has its warts, but I make my living in PHP which is practically made of warts. But the python ecosystem still seems to be trying to figure out this whole package management and project setup thing. In most languages I can do some form of `$blub install` where $blub is the language's official package manager or some close equivalent. It's just python that always screams at me that I have to set up and "enter" a virtualenv. I get what venv is for, but it's still a weird hack of hardlinks and relative paths that no other language seems to need, and a clumsy two-step dance of a UX that hasn't improved in like 20 years.
necovek1 day ago
I am not sure you get what virtualenvs are: Python is never screaming at you to set up a virtualenv, it must be a particular package recommending use of virtualenv for easy set up without interfering with the rest of your system.

Virtualenv allows you to seamlessly run multiple Python ecosystems simultaneously, even within the same project directory. It's basically primitive containerisation mechanism that predates any actual containerisation systems on Linux.

You do not have to use it, but then you can easily slip into a sort of "DLL hell" (multiple incompatible library versions installed system-wide) with multiple projects — or need to bundle all dependencies within your project directly. None of this is specific to Python, really — any shared library system has the same challenge. How many other systems are there in active use making it as easy to use multiple incompatible versions of shared libraries per project or within the same project?

When in doubt, you can always retreat to the basics in Python world: put packages you need in a path of your choice, and point PYTHONPATH (sys.path) at it.

nijave1 day ago
What are the problems you have with tooling? Imo it's no worse than most other languages besides a very small handful of recent ones (rust, go) where everything is included

The easy approach is usually just throw it in an OCI container

There's not much concrete to go on here besides "I don't like the ergonomics"

bborudabout 13 hours ago
Throwing it in an OCI container is not the "easy" approach. It is the beginning of opening a can of worms. I know, because I've been developing tooling to do that at scale for the last few months and behind every layer of worms there are uglier and bigger worms that you need to deal with.

And yeah, I have written a lot of code to insulate Python in containers while allowing meaningful access to hardware and services. While at the same time not heaping more complexity and cognitive surface at the developer. Including writing my own container software to actually understand what's involved at a more detailed level so I know what I'm doing when trying to make this work with existing container software. (No, I don't run any of my container managers in production since I don't want to maintain it -- but this also means a bit more complexity in using existing ones)

It may be "easy" in trivial cases, but it is very far from easy if you want to make something that can cater to a wide range of scenarios.

Hizonner1 day ago
Yes, but unfortunately Python has invaded everything, and one must adapt.

Python is going to be preinstalled on almost any machine I use, with a reasonable assortment of libraries. And even if they're not preinstalled, the libraries I want are likely to exist. They'll have unstable APIs and weird quirks, and I'll have to take my choice of bad packaging systems to install them, and everything will just generally be a pain, but they'll exist and largely work. That's not true for any language I actually want to code in. I mean, I'm not going to deny that Python is better than shell scripts or (usually) C.

It's not like it's a pleasant language to code in, especially if you actually want to use the type support, which is weird and irregular and keeps changing and has to work around fundamental design problems at the core of the language.

IshKebab1 day ago
Yes. If you have a choice.

For people who don't have a choice, type checked Python is better than nothing.

locknitpicker1 day ago
> If you are going to be super-strict with type-checking, wouldn’t it be best to switch to a statically typed language and get the performance gains as well?

I don't understand your question. The whole point of static code analysis is preventing bugs. Don't you like Python code to not have bugs that are easily caught with static code analysis, or is preventing code a foreign idea that is better left to other languages?

necovek1 day ago
I don't understand your question. Are you saying static code analysis is impossible without type declarations? None of it?
globular-toast1 day ago
Show us the language and we'll all switch tomorrow.
thrance1 day ago
Often, when I code in Python, it's because there are some libraries that aren't available in whatever other language would have been my first pick. Then, typing and type-checking are useful tools to stave off the codebase turning into the unruly mess that all Python projects eventually become.
necovek1 day ago
Don't projects in all languages turn into unruly mess by default?

It requires special care to not let long-running projects evolve into it.

Python is only special in that it is extremely productive and allows lots of easy evolutions of a project with not a care in the world, so the timeline is probably shorter on getting to the "unruly mess" if no special care is put in to make it survive many evolutions.

Perhaps a bit special too in that it looks welcoming to the masses who have no idea how software systems evolve and thus do not even know there are some special patterns to introduce for code to survive many changes in the future. I am undecided if this is a pro or a con of the language itself.

scuff3d1 day ago
Seriously, just switch to Go or something
kingstnap1 day ago
The fact that this article seems to honestly recommend people run 5 different type checkers on library test suits really reflects the tacked on feeling of Python typing.
vitorsr1 day ago
I am not sure it is recommending more than it is commenting on the current state of developing public-facing APIs in Python.

The downstream users that import the package either have to ignore checking its exported types altogether, manually stub it, or have a subpar development experience to varying degrees.

This is something I saw the other day with some package that provided comprehensive stubs for an untyped library. The .pyi file was littered with comments about quirks from the numerous type checkers (five now).

ActorNightlyabout 23 hours ago
No, it reflects the nature of misunderstanding Python by people who think their system is better, have no idea how Python in production actually works, and just publish things like the article to make themselves feel better.

Typing is not a huge issue, period. In Python, if you pass a wrong type to something, program just throws exceptions. Exceptions are not the end of the world like people make it seem. Functionally, finding errors during the process of taking code and compiling it with type checking is no different than taking code and just running it against a set of tests, which every production code has (or should have)

The only waytyping ever saves you from it is by being absolutely strict - every type defined has a finite range of values, and every operation has bounded domain and range. I.e if you have a string field, its not enough that its a string, you also must define the total number of characters that string can have, and values for each character, along with more complex rules on sequences of characters.

If you have this system, (something like Coq comes close), then if your program compiles, its by definition correct. But even the strongest proponents of typing don't really want to do this, because they realize how long it would take to write code.

The simple truth is that Python is easy and flexible enough to work in that you don't even need type checking. An LLM can effectively function as a type checker for you if you care enough. For any errors that you encounter due to lack of typing, its ultimately way faster to fix with Python than it is to spend time writing strongly typed language.

TremendousJudge1 day ago
It's ridiculous. They should have made it an explicit part of the language. The interpreter knows about types already, it's crazy that they couldn't just let the user make the types explicit rather than implicit, and have the interpreter enforce that.
mort96about 15 hours ago
The interpreter knows types at runtime, not at parse/compile time. The interpreter already does a lot of dynamic type checking. It has a much stricter type system than e.g JavaScript; JavaScript will pretty much always convert operands to produce some result (even if it's just NaN or the string "object Object"), while Python will often just give you a type error.

The interpreter doesn't know about static types.

I agree that they should've made typing more a proper part of the language and not left it in this weird half-defined state of "standard syntax and some standard typing imports but undefined semantics". But it's not just a matter of enforcing existing types.

voidUpdate1 day ago
> "In Python, any method __eq__ is expected to return bool, and if it doesn't, then we need to explicitly tell type-checkers to ignore the type error. This function in Polars can also return different types depending on the inputs, thus requiring overloads."

Why would you ever want a == b to not return a bool??

EDIT: Yes, I understand that you can do element-wise equality checks on numpy arrays now

vitamark1 day ago
There are examples like ORM query builders (something like `User.id == user_id` should not return a boolean, but rather some inspectable query part), multi-value comparisons (e.g. numpy arrays and views which could also be used as masks for indexing)

In general, when you get your hands on operator overloading you get a bunch of various quirky applications for each. Some dunder methods have strict runtime-level rules (e.g. __hash__ or __len__), some don't

brandnewideas1 day ago
That's one of the things I truly didn't get from my (very limited) experience with SQLAlchemy. Why not just have a method like orm.eq(User.id, user_id)? Much more readable.
samsartor1 day ago
Elementwise equality! Given two dataframe columns or ndarrays, users often expect `==` to give out a column or ndarrays of bools (like `+`, ``, `*, `&`, and just about every other binary operator).
stephbook1 day ago
What I love about operator overloading is that now you can't use operators without looking at their definition, in which case.. you could have done numpy.equals(a, b) anyway.

Does a == b true, if all elements are the same? Does it return an array of booleans? It's anyone's guess!

NewJazzabout 22 hours ago
What's fun is that it could return an array of false if all elements are different, and then that value is truthy.
tzsabout 24 hours ago
The function call approach can be a lot less readable.

Consider using Shamir secret sharing to share a secret, D, among several people with two people required to recover the secret. D is a positive integer, such as a randomly generated 128 bit AES key you are using to encrypt your launch codes or credit card database.

For anyone not familiar with Shamir secret sharing what you do is pick a prime number, p, that is larger than D and another random positive integer A, that is less than p. Then give each person a pair of numbers, (i, (Ai + D) % p), where each person gets a different i (which should be a positive integer less than p...it is OK to simply use 1, 2, 3, ...). Let's let Di = (Ai + D) % p.

(This is for the case where you want any two people to be able to launch your missiles or decrypt your database. If you wanted 3 required instead of giving out (i, (Ai + D) % p) you would give out (i, (Bi^2 + Ai + D) % p) where B is a randomly chosen positive integer less than p. For 4 required add on a Ci^3 term, and so on).

Given (i, Di) and (j, Dj) and p it is possible to recover A and D.

Here's what that looks like in a language where the big int library uses an accumulator style, i.e., operations are of the form X = X op Y, where the ops are methods on the big int objects. Assume Bi and Bj are big int objects initialized from i and j, and Di and Dj are already big into objects, as is p. This particular example is using Perl. (This is very old code. Since 2002 you can add a "use bigint" pragma to Perl code and then it would look a lot more like the second Python example below).

  my $A = $Dj->copy()->bsub($Di);  # Dj-Di
  $Di->bmul($Bj);                  # j*Di
  $Dj->bmul($Bi);                  # i*Dj
  $Di->bsub($Dj);                  # j*Di-i*Dj
  $Bj->bsub($Bi);                  # j-i
  $Bj->bmodinv($p);                # (j-i)'
  $Di->bmul($Bj);                  # (j*Di-i*Dj)*(j-i)'
  $Di->bmod($p);                   # (j*Di-i*Dj)*(j-i)'  mod p
  $A->bmul($Bj);                   # (Dj-Di)*(j-i)'
  $A->bmod($P);                    # (Dj-Di)*(j-i)'  mod p
At this point, the recovered A is in $A and the recovered D is in $Di

Here's what it looks like in a language with the ops as function calls taking the big int objects as arguments. This example is Python without using operator overloading.

  import operator as op
  def recover(i, j, Di, Dj, p):
    j_i_inv = pow(op.sub(j, i), -1, p)
    A = op.mod(op.mul(op.sub(Dj, Di), j_i_inv), p)
    D = op.mod(op.mul(op.sub(mul(j, Di), op.mul(i, Dj)), j_i_inv), p)
    return A, D
Probably more readable than accumulator style. Here it is in Python using its built-in operator overloading for big ints:

  def recover(i, j, Di, Dj, p):
    j_i_inv = pow(j-i, -1, p)
    A = ((Dj - Di) * j-i_inv ) % p
    D = ((j*Di - i*Dj) * j_i_inv) % p
    return A, D
I'd sure rather come across that than either of the earlier examples.

OT: this reminds me of something I started to do once but never finished. I was going to write for each language we used at work that had a big int library but that did not support operator overloading a class that implemented a big int RPN calculator. Java, for example. Then recover would look something like this:

    calc = new BigRPNCalc();
    calc.do(j, i, "-", p, "modinv dup");
    calc.do(Dj, Di, "- *", p, "mod swap");
    calc.do(j, Di, "*", i, Dj. "* - *", p, "mod");
    D = calc.pop();
    A = calc.pop();
But I never ended up needing big ints in any of those languages so never really got past some initial design work.
datsci_est_20151 day ago
One example is if an and b are arrays (e.g. numpy arrays) it’s not unreasonable for dunder eq to return an array of booleans.

Another example might be if you have a domain specific representation of equality (e.g. class Equality)

voidUpdate1 day ago
I can see the first one making sense, but why would you need a representation of equality other than "yes, these are equal" and "no, these are not equal"?
agons1 day ago
The first use case that comes to mind is if you want a DSL to build expressions that are evaluated later in some different context e.g. when using `polars`:

```python df.filter( pl.col("foo") == pl.col("bar"), ) ```

Sqlalchemy does something equivalent too, and I'm sure there are many others.

datsci_est_20151 day ago
Well personally I’m not a fan of turning everything into an object, but if you have properties or methods that exist upon the concept of Equality you might want to encode directly onto a class. Maybe in a domain where “Equality” is an important concept, like mathematics or even something like accounting.

Could enable a different interface into approximate equality for floating point numbers: Equality.approximate(iota: float) -> bool

olooney1 day ago
It could return a vector or a deferred expression? In polars, for example, operations on `pl.col` return `Expr` objects that are used to build queries, not immediately evaluated:

    df.filter(pl.col("status") == "active")
In numpy, `x == y` return a boolean vector of the same shape as x and y, comparing them element-wise.
kuschku1 day ago
Primarily, because Python doesn't have quasi-quoting. You can't pass an expression without workarounds like this.
xemdetia1 day ago
I thought JavaScript language equality quirks was seen as problematic not a missing feature in Python.
voidUpdate1 day ago
At least in javascript, it tells you if things are equal or not. In python, apparently you could answer if A is equal to B with "beans" or 17 or ['a']
hmry1 day ago
Never understood this complaint about operator overloading.

In any language, a function called `isEqual` could wipe your hard drive and replace your wallpaper with a photo of a penguin. Therefore, letting programmers pick the names of their functions is bad? No, obviously naming things for least surprise is the programmer's responsibility.

But when it's the symbols `==` instead of an ASCII name, it's a problem in language design?

(FWIW in Javascript, being unable to override == is actually a problem when you want to use objects as Map keys)

throwaway8943451 day ago
Python never met a footgun it didn’t need to adopt. In this case, however, it’s not equality checks, but operator overloading. I was a Python developer for a decade before switching to Go and life on this side is so much better.
data-ottawa1 day ago
Operator overloading has never been an issue for me, but terminating a line with a comma creating a tuple, or white space (including new lines) between strings to concatenate have cost me days of work over the years.

I understand why those exist, but they’re pure evil.

throwaway8943451 day ago
IIRC, SQLAlchemy overloads this to return an object that represents an equality check in SQL. Because it was returning an object, it was always evaluating to True, because of another of Python’s footguns: truthiness/falsiness. This was a decade ago, and these particular footguns were not even remotely the biggest culprits in our bug backlogs (another honorable mention includes accidentally calling a sync function in an async context, causing timeouts in unrelated endpoints and leading to cascading system failure).
VoidWarranty1 day ago
Dynamically typed languages are going to decline with the rise of AI coding.

Statically typed languages provide the determinism necessary to efficiently anchor probabalistic coding agents.

You can throw as much type checking at dynamic languages after the fact, but youre just going to burn energy (and tokens) doing what another language gets 'for free'.

necovek1 day ago
I am not so sure that's this simple.

No matter your preference, programs in dynamically typed languages are still very much deterministic.

To be able to reason about the output of LLMs (though it is debatable how often will this be needed), you want the output from your imprecise human language spec to a deterministic spec (code) to be as easy to review as possible (for correctness, but mostly for any glaring errors). With proper setup, ensuring correctness of one deterministic output (Python) in comparison with another (eg. typed language like Rust) is just a deterministic run away (a test suite) that should not use any tokens from the LLM, and should have no practical differences in compute use.

fasterik1 day ago
Static type checking catches a whole class of programming errors for free. Writing tests costs tokens or human time, so you end up needing more code (and probably more CPU time) to achieve the same level of error-checking in a dynamic language.
necovek1 day ago
Very simplistic look, IMO. I'll add another one mostly as a counterpoint (not that I believe it is strictly true, but largely yes!).

Python is a lot more expressive than other languages and has a very terse syntax, and thus requires LLM to output much fewer tokens to achieve the same job compared to other languages.

Adding a few more tests to ensure data conformity on top of what you have to do anyway with a statically typed language still results in fewer tokens overall.

Stevvoabout 17 hours ago
Maybe, but AI models are better at writing python and JS than any other language. Probably because they are the most common and thus had the most code available for training.
big-chungus4about 17 hours ago
Or attach an LSP server with a type checker and require the model to produce strict strongly typed python code
AsthmaBoy1 day ago
Python is dynamically, strongly typed. It cares a lot about the types of its objects, you can't just mix and match at will.

Perhaps you meant statically typed?

VoidWarranty1 day ago
Oops thank you, I did mean static. I should know better as I am a cpp fan. Updated.
ActorNightlyabout 23 hours ago
This is probably the most intellectualism ive seen anyone put into a comment that is so very, very, obviously wrong.

Yeah, in the age of AI where the whole goal is to not have to think, type as fast as you can with misspellings, and copy paste stuff without thinking, its TOTALLY a better system to worry about the types of whatever you are feeding into llms.

VoidWarrantyabout 22 hours ago
Its about limiting surface area.

The gymnastics people are putting their ops teams through in order to validate oceans of generated slop is insane. Just use Rust and half of that work goes away.

ActorNightlyabout 4 hours ago
Please stop trying to make Rust happen. You guys are reaching SO hard now.

There is no way that Rust will be faster than simply specifying tests for an agent to run after it has generated code.

blahgeek1 day ago
> Prioritise running as many type-checkers as possible on your test suite. Run at least one on your source code.

There are two types of tests: those that test against the public API, and those that test internal codes with various mocks and fakes. I think the vast majority of unit tests is the latter one, in which case the suggestion does not really make sense.

KolmogorovComp1 day ago
Why anyone would still use mypy besides legacy infrastructure is beyond me. It is dog slow as well as being the laziest of all, not catching many mistakes.

Unfortunately for Django apps switching to any alternative leads to the dreaded “wall of errors” issue. If anyone got to work this out in the past, I’d gladly take advices.

thr1owaway96211 day ago
I use pyright with a 50k LOC Django REST API codebase. I haven't really had problems. From my pyproject.toml:

django==4.2.30

djangorestframework==3.16.1

---

django-types==0.15.0

djangorestframework-types==0.8.0

pyright==1.1.390

My dj version is pretty old, but I'd assume things have only gotten better since v 4?

cptmurphyabout 16 hours ago
The django mypy plugin can inspect django models types, project settings and much more context.
Syntafabout 14 hours ago
We switched our very large Django monolith codebase over to ty — the trick for us was generating stubs for Django models and having tooling keep those stubs in sync with the actual models.

Went from type checking taking ~10 minutes in CI to now taking ~15 seconds and runs on pre-commit.

Absolute game changer, I think we spent $10k in claude credits and did the entire mypy -> ty refactor in about 3 weeks.

emerybergerabout 8 hours ago
Since we are talking about Python type-checkers: we've built a (non-AI based) type assistant for Python called RightTyper (https://github.com/RightTyper/RightTyper). Below is a brief description; a technical paper describing RightTyper is here: https://arxiv.org/abs/2507.16051, "Getting Python Types Right with RightTyper"

RightTyper is a Python tool that automatically generates type annotations for your code. It monitors your program as it runs and records the types of function arguments, return values, local variables, and class fields — with only about 25% runtime overhead. This makes it easy to integrate into your existing tests and development workflow, and lets a type checker like mypy catch type mismatches in your code.

stephbook1 day ago
Five different type-checkers and even type-checking projects think adding multiple "ignores" is sound code. Typescript would allow overloads without ignores, for example.

Python's type checking ecosystem truly is a mess.

semiinfinitely1 day ago
Everything that isn't uv, ty, ruff is wrong and deprecated
pvdebbe1 day ago
Will you update the list for us, a month from now?
throawayontheabout 17 hours ago
i doubt uv and ruff are going anywhere that quickly at least
throwawayffffas1 day ago
The whole type checking experience in python has disappointed me deeply and is seriously affecting my work.

I see the appeal for type-checking and yeah it has caught many bugs. But the language is quickly running blindly to the worst of all worlds in regards to typing.

1. You have to exhaustively write types in many cases where they can be obviously inferred.

2. The type checking is just a lint step. i.e. we are still paying for the duck typed typing system.

3. We no longer get to use the duck typed typing system making a lot of generic code require obscure annotation incantations to pass the lint check while it's correct python code.

My ideal typing system would be around constraints introduced by the code and completely inferred unless the user wants to tighten the constraints. i.e

Instead of

  def foo(a: int, b: int) -> int:
    return a + b
You would write:

  def foo(a, b):
    return a + b
And upon checking if you tried to do foo(5, {})

It would tell you that there is no + operator for int and dictionary that is required by the foo function.

My ideal typing system would allow you to constraint the types as well like so

  def foo(a: int, b: int):
    return a + b
The return type is not required in this case because it can be inferred by the function definition. For other cases it could be defined as well to constraint that we don't want None for example.
HelloNurseabout 16 hours ago
Declaring types only where one wants to introduce a constraint would be nice, but unfortunately calls with obvious wrong types like "foo(5, {})" are a minority; in most cases types in a call depend on types of the calling function's parameters, and the type checker can only deduce unknown type from unconstrained type.

Moreover, the extremely dynamic execution and lack of compilation in Python means that determining in a lint step whether "there is no + operator for int and dictionary" is essentially impossible: if you find some __add__ or __radd__, you need to trust its declaration that it accepts certain types or not and hope it's still there at runtime; if you don't find it, it might exist at runtime when operator + is actually used.

A compiled and sufficiently static language, on the other hand, can inspect source code and libraries and reason about potentially infinite sets of definitions and prove that functions exist or don't exist.

throwawayffffas16 minutes ago
I have the sneaking suspicion that for the vast majority of code it could be done.

I don't think the operator overloading is such a big issue since you can do the same kind of tracing for the overloaded operator.

The main issues would be metaclasses and all sort of monkey patching etc. i.e. anything that could mutate a type at runtime because the linter can't know if the mutation has happened.

Unfortunately I do not have the time nor patience to introduce the sixth type checker.

rzmmmabout 11 hours ago
Ocaml is probably closest to that. The ML language family has roots in generic code with global type inference.
leni5361 day ago
This is what the typing spec says about type narrowing[1]:

"Type checkers should narrow the types of expressions in certain contexts. This behavior is currently largely unspecified."

Have fun.

[1] https://typing.python.org/en/latest/spec/narrowing.html#type...

Advertisement
zerof1l1 day ago
From my experience with Python, both personal and professional, I find it immature and not well-suited for large codebases. Typing should have become part of the language a long time ago; it is clear that users want it.

Take, for example, PHP… look at the features released in the last 6 or so years, starting with PHP 7, and how mature the language has become.

With the advance of AI-assisted programming, I feel like Python is always a bad choice.

__mharrison__1 day ago
I'm happy w/ ty right now. My agents runs it fast and it seems to provide great guardrails.
braiamp1 day ago
What is this saying differently from https://peps.python.org/pep-0827/ ?
ocamoss1 day ago
Everything? The blog post and PEP have almost nothing to do with each other lol
ryanshrottabout 21 hours ago
I run pyright in CI and mypy locally. They catch different things - pyright is stricter on overloads, mypy catches more None issues in our codebase. Annoying, but I have not found one that covers both.
ghostly_s1 day ago
Why would users care if you're using the same type checker as them? Surely they're not expecting all their imports to be instrumented for running redundant types checks?
Someone1 day ago
Users do not care about that, but they want to not see type errors or warnings when they integrate your API in their code.

That’s why you want to run their type checker on your API. you cannot know what “their type checker” is, so you want to run all popular type checkers on your API.

ForHackernews1 day ago
Sounds like a them-problem. Their type checker can accept my declared typings for my public API, or they can override it with their own custom type stubs if they have objections.
prodigycorp1 day ago
what are ppls' impression of pyrefly? i've become completely captive to uv's tooling. it has allowed me to think only about coding versus tooling. dont feel like giving another typechecker a chance unless it offer's something i'm not getting from ty.
shermantanktop1 day ago
That blog needs to run a AI checker. Content aside, a lot of the writing is pure AI style.

> The type checking that matters most (and why you've probably got it backwards)

Honestly, I don’t care if the author got some AI help. But that click-bait style is ubiquitous and obnoxious.

insane_dreamer1 day ago
I use ty with Zed.

But PyCharm's built-in type checker is far and away the best that I've used with proper type inference through multiple class inheritance hoops.

shevy-java1 day ago
The type-lovers will be angry! :)

The blog entry fits into ruby too, to some extent; while the situation is nowhear near as bad as in python, you have the same question-marks why types suddenly emerge out of nowhere. Almost ... almost as if some people have a specific agenda, and try to pull through with it.

Well, there you have it - the type-addicted people are ruining python.

woeirua1 day ago
With agents it no longer makes sense to tie yourself to Python's archaic development experience. How many type checkers are there? Package managers? Don't even get me started on cross-platform deployment.

Strongly typed, compiled languages have never been easier to use, and agents reap huge benefits from the tight feedback loop that the compiler provides. Moreover the benefits of the Python ecosystem are less significant today than anytime in the past 20 years. Need something that's only available in Python? Just point some agents at it and you can port it.

voidUpdate1 day ago
What about the several people worldwide who don't want to use LLMs to program?
cryptonym1 day ago
They also "reap huge benefits from the tight feedback loop that the compiler provides".

When something is easier/requires less context, it tends to work well for both human and LLM.

vips7L1 day ago
I've noticed this a lot in LLM generated Java. Since it doesn't know what can or can't be null it tends to wrap everything in Optional<T>. Super strong type systems are becoming even more important.
datsci_est_20151 day ago
> Just point some agents at it and you can port it.

Don’t think we’re there yet, otherwise we would see a bunch of forks of major libraries to alternative languages - and not just Python. There’s still too much risk of insidious errors and bugs.

hedora1 day ago
I've done thus a few times for stuff in the < 10,000 LOC space. It works great.

There's something particularly satisfying about shipping a 1-10MB static rust binary instead of a 2GiB docker python environment.

(I'm talking about just porting simple applications, or maybe a missing package/crate at a time. Not both at once, and not typical 100K-10M line internal legacy sprawl)

datsci_est_20151 day ago
What have you ported, for example? Any 3PL or all internal code?
Advertisement