Hacker Newsnew | past | comments | ask | show | jobs | submitlogin
Untyped Python: The Python That Was (pocoo.org)
72 points by vaylian on Dec 2, 2023 | hide | past | favorite | 62 comments


"But hear me out: all the arguments against dynamic languages and dynamic typing systems were already there [in 2015]! Nothing new has been invented, nothing really has changed."

I think what changed is the static languages got a lot better. Static languages when Python was invented were awful. Now I can write very sensible code in static languages that looks almost as nice as dynamic code, while still carrying all the benefits of dynamic languages. That's what flipped the cost/benefit tradeoff for me; yeah, I'm personally a bit grumpy about Python just getting more and more complex, but if it was 2023 Python versus early 2000s static languages I'd still be using Python.

But that's not the choice I face.


Not only the static languages, but the tooling around them; better type-driven autocomplete and solid support in editors that aren't as heavyweight as Visual Studio or Eclipse [1].

[1] I think that's what was popular in the Java world at the time? I'm not sure on this.


ML & Hindley Milner type inference was invented in 1978 or so. Static languages have been good for a very long time.


The point is that most of the popular statically-typed languages (C, C++, Java, C#) didn’t have any type inference when python got really popular.

(Edited from “was invented” to “got really popular”… python was invented before Java or C#… Type inference in these major language didn’t happen until circa 2010 or later.)


Come on. I know what early 2000s Haskell was like. It's a slim minority view even on the Haskell reddit that it was/is production-ready for anything, and it certainly wasn't useful for networking and had a lot of other significant issues... and it would be the premiere Hindley-Milner language available at the time. It is missing a ton of things that Haskell programmers would consider basic necessities now, even in the language itself, to say nothing of libraries. Haskell slipped from research language to production-ready for at least many tasks very gradually, so it's hard to put a date on it, but that date is definitely late 200x at the earliest, and mid-2010s is probably a lot more realistic.


ML (later StandardML) is a very nice language which has been around since the 70s.

https://en.wikipedia.org/wiki/ML_(programming_language)

Significantly before Haskell, and certainly worth looking at.

I'm not sure why it lost in the popularity wars. Maybe it lost to C/Pascal/C++ because it had garbage collection and then never regained its footing.


I have never seen a serious case that ML is production-ready, not in the early 2000s and not now.

I do not mean this as some sort of major criticism or anything, just as a brute engineering fact. It's not like Haskell has a huge suite of programs I can point at that people use because they are legitimately useful and not because they are Haskell programmers (Pandoc, XMonad, and honestly I'm already coming up a bit dry here), but I can still point to more than I can ML, which is a flat zero.


Heh, that's probably true about being "production-ready", but I think you're conflating quality with popularity, and there are plenty of counterexamples to that. The point was that there have been good staticly typed languages for a pretty long time now.

I guess we could debate what "good" means...


No, I'm not conflating quality with popularity; I am telling you the definition of the term I am using. You are welcome to your own definitions but you need to label them as such.

There was no option in the early 2000s of deploying any significant production software in ML. You can see this very easily, let me make a list: C++, C, Java, ML. Which one doesn't belong to the set "production-quality language in 2001"? Anyone who can't answer that is just being deliberately obtuse. Even though Java was jammed into that list more by executive fiat than anything else, it was still in that list. You may like ML a lot, you may have preferred to work in it, but it is very clearly not able to run in the same spaces as C++/C/Java.

I used Haskell circa 2012 to build a highly concurrent network server for a game contest. I'm glad it was just a game contest and the biggest stakes were which college student got the grand prize. It crashed under load. Really quite a bit; I was restarting it about every hour, under what would have been a trivial amount of load by any production standard. And it was just Haskell bugs; as Haskell advanced, the exact same code crashed less and less. Probably it would be fine today. But this is the sort of thing I mean; even if you like the language, production shines a really, really harsh light on a language. Having a theoretically superior concurrency solution didn't make my code not crash the runtime. Having a theoretically superior typing solution still leaves a language needing tested libraries, tested framework, exposure to real world situations. The odds of hitting a critical snag is just too high otherwise.

Even today, telling me that you're building a critical system in ML would basically be a firing offense (if you obstinately stuck by it). I'm as open minded about languages as anybody, but I know enough to know with close-enough-for-business-certainty that it's a bad business decision. Should the ML community suddenly revive and fix up the language over the next 5-10 years, I'd happily reconsider, but today? No question. Even Haskell would be a very uphill battle, for many good and sufficient business reasons, but maybe you could just about convince me there's some task you just need it for.


I can't tell if you're spun up or not, but from the tone and choice of words, this doesn't seem like a friendly conversation any more. Take care, and I won't reply any further.


(off-topic/a bit of personal history) I was in college around those years, and working on the side as a consultant [dotcom boom=companies were crazy for anyone with some experience]. I had a infatuation with programming languages and got proficient enough in 14 of them to write non-trivial code: a toy language (logic language, prolog/datalog) and problems from my consulting gigs, IIRC network flow analysis from logs, and relational database sync with data compression (a client had sales agents[1] on dial-up; I wrote their app in Delphi, but was struggling to sync without errors and fast enough).

(start here to not get bored) Haskell wasn't definitely ready for enterprise. It felt very researchy.

I do remember other functional languages with HM-style types that felt more polished and ready: e.g. I did learn Ocaml, or Concurrent Clean.

https://en.wikipedia.org/wiki/Clean_(programming_language)

Jane Street adopted Ocaml in the mid 2000s.

https://blog.janestreet.com/what-the-interns-have-wrought-20...

"Yaron Minsky joined Jane Street back in 2002, and claims the dubious honor of having convinced the firm to start using OCaml."

https://www.janestreet.com/tech-talks/jane-and-compiler/

"that’s when we made a real kind of fateful step into integrating OCaml into our permanent infrastructure. And that was like in 2005, we started doing this 2006, we had about 10 people who were programming in OCaml"

(more off-topic) [1] I really liked Ocaml... but, sadly, I discovered it only after I finished that sales agents system and the final version was in Delphi for the main app and Dolphin Smalltalk for the sync part (I needed metaprogramming to pull it off).


What are your preferred typed languages?


No them, but my favorite is Scala. It can look as clean as Python, especially with Scala 3 which makes braces optional.

But then I have the type system, which saves my ass every day. Maybe I am just an old dog who can't memorize complex code bases like in my youth. The good thing is that I no longer need to do that.


> There was a stellar developer experience. Yes it did not have intellisense, but all the changes that I did appear on the web instantly.

> ...after I deployed bad code and it blew up in someone's face, I got an email that not only shows a perfectly readable stack trace, but also a line of source code for the frames.

This is an odd contradiction. On one hand, he values the fast feedback loop you get from edit-and-reload style development, but on the other, he doesn't appreciate the instant feedback from the type checker saying it's not gonna work.


It's a contradiction I see in this discussion quite often.

If you want "quick feedback loops", a type checker is probably one of the quickest. Almost always quicker to give feedback than tests even. And certainly quicker than ftping code to a webserver and clicking through a webapp manually.


Modern JS (actually TS) tooling has adopted a middle ground. There is an instant feedback - un-type-checked version that runs (typically used in dev mode) with instant-feedback/hot-reload, and a slower compile and build flow that builds for production.

I was initially not very convinced of the benefits - why have a type-checker if you won't use it in the small? But IDEs provide much of the benefit inline while writing code and this is seldom a real problem from the DX perspective.

My skepticism was unwarranted.


With Python + mypy you get this middle ground as well. No need to type-check before you run your code, if you don't want to.


> we no longer trust developers as much and we are re-introducing the complexity that we were fighting

Says who? Or is it: we're way past the toy stage of web apps with their trivial CRUD forms, so the code base grows, complexity is unavoidable, and untyped languages are a bad choice under those conditions?

> Java did not even have generics.

Java got generics in 2004 (when the author was 15?).

> The core principles of [using typing] have not altered: types add value and they add cost.

And that's the conclusion? I'm flabbergasted.


The author started programming in 2002 it seems[1]. But probably takes time for things to proliferate. I’m sure you could also see the appeal for a kid trying to test their abilities to have fewer guardrails.

But yea I also don’t know why you’re responding so strongly. He basically admitted he didn’t have a strong opinion for either side, and was mostly musing on a vibe that seems to have moved on.

He’s mostly developing in rust these days, so I’m sure he knows the value in good typing.

Lastly, anecdotally, I’d agree with his observation about trusting programmers less. There is definitely a safety-ism culture. Probably from massive professionalization of the web dev space. Again, anecdote.

1. https://www.quora.com/Armin-Ronacher-How-did-you-start-out-p...


> Here is what has changed: we no longer trust developers as much and we are re-introducing the complexity that we were fighting. Modern Python can at times be impossible to comprehend for a developer. In a way in some areas we are creating the new Java. We became the people we originally displaced. Just that when we are not careful we are on a path to the world's worst Java. We put typing on a language that does not support it, our interpreter is slow, it has a GIL.

If you believe type hints in Python are a net negative, then just don't use them.

I also don't get the performance argument: Type hints aren't evaluated at runtime, so whether code has them or not shouldn't matter for performance.


I agree with you, but for correctness' sake:

> I also don't get the performance argument: Type hints aren't evaluated at runtime, so whether code has them or not shouldn't matter for performance.

Type hints actually are evaluated at runtime, since they're part of the syntax tree. They just aren't checked for correctness, so the (small, but sometimes non-negligible) amount of parsing overhead they add is essentially lost performance.


If the negligible additional parsing overhead makes or breaks your program, you shouldn't have been writing it in Python in the first place.


I agree. I don't think it's a very good argument for a language or ecosystem like Python. But it's important to be right factually about it not mattering, versus claiming that it has no performance impact at all :-)


It might not be so small, if you import a module just to declare a type, that's an import that happens at runtime, that might not be needed.

The correct way would be to do conditional imports that are only performed while typechecking in this case. But I'd expect the issue to happen often.


In Python you can use this:

    from __future__ import annotations
    from typing import TYPE_CHECKING

    if TYPE_CHECKING:
        import mymodule

    def myfunc(arg: mymodule.MyType) -> bool:
        return bool(arg)
This will make type annotations to be converted internally to strings. TYPE_CHECKING is a constant that's always false except when you use a type checker, so mymodule will never be imported on runtime, but the type checker will think that it was imported.


    from __future__ import annotations
This will break all the runtime type checking modules (such as typedload, which I wrote).


> This will break all the runtime type checking modules (such as typedload, which I wrote).

Most runtime type checking modules I am aware of support

  from __future__ import annotations
E.g., typeguard, Pydantic, beartype, among others.


they "support" it, but many things will break, for the simple reason that there is absolutely no way to resolve certain types at runtime. In fact the main reason why it's a future is because the pydantic people complained that it broke stuff.

When I had complained earlier, Guido didn't give 2 shits, although it broke typedload, a library I wrote that is very similar to pydantic, when pydantic didn't exist.

So I know what I am saying


Are they evaluated at runtime? Or just when the bytecode is generated? (Is that considered runtime?)


They're evaluated at runtime, but CPython never checks if type hints are right or not. If you use "from __future__ import annotations" then they're converted to strings, so they could even have references to objects that don't exists (this is for a better performance).


I don’t agree with all of his points either but I think what he meant by that was a separate, semicolon’d statement that the interpreter is slow (not that typing is slowing it down). I think he’s saying that types are useful for performance optimizations in other languages but Python is not meant to be that.


> we no longer trust developers as much and we are re-introducing the complexity that we were fighting.

I hate this spin to somehow make it about not trusting developers to do their job right. As if it was an insult to test for correctness. Imagine skipping pre-flight checks for accomplished pilots, or load calculations for renowned bridge engineers.

This hubris of some software developers assuming to be hyper-correct geniuses really alienates me.


[flagged]


Most people just want to read info, enter their data, get feedback and go on with their day. They don't care about smooth transitions, spa, etc. The only thing they care about is if it works correctly and their expectations are met.

There are applications where all the unnecessary abstractions might make sense but those are the exception not the rule.


> Modern Python can at times be impossible to comprehend for a developer. In a way in some areas we are creating the new Java.

I've made this comment or something like it about other languages and maybe Python in the past:

There is a fundamental conflict between simple languages and "enterprise" languages. By "enterprise" I mean languages used for large-scale projects by large teams-of-teams who need to integrate work without having (or not easily and readily having) direct access to each other. Once you start trying to work on a large system or collaborate but via specs, the game changes.

Python started as a teaching language, a next-gen version of ABC. It was meant to be simple and accessible for learners (see also BASIC, Pascal). Once it started getting used in production, users started hitting walls and early design decisions were creating problems. The language was extended and altered (see also variations of BASIC, Pascal) to better support these new use-cases while also trying to maintain the simplicity of the original. That got us Python 2.0 and later Python 3.0.

To some extent you can maintain simplicity while adding new capabilities if they are optional (or used by the simpler interfaces so the users don't see them if they don't want to). Like having all the dunder methods or even decorators (creating your own). The type annotations are similar. They're a useful feature, and totally optional for the vast majority of users. But they're useful for the case of people going beyond small teams and small programs. They're useful for scaling up and integrating with other efforts because they provide another point of shared communication about what is expected from the program's interfaces.

But yes, Python becoming more like Java should be expected. For all the flak it gets, a lot of what went into designing Java was targeted at scaling the development effort. If you want to scale, you will end up imitating some aspects of Java, Ada, and other languages designed (initially or over revisions) for that purpose.

Maybe they should have pulled a Wirth. Changed the name so that instead of Python 2 (and 3) we had a renamed language built on many of the same concepts and syntax. But they didn't, and it's no use crying now.


> Here is what has changed: we no longer trust developers ...

I have a different hypothesis on this (never checked it though). I think as those languages - Python, JS were getting more and more popular we started using them more and more to build and maintain more complex projects. What worked for a few glue-code scripts doesn't work for a Dropbox size and scale project. Thus there was a need to evolve and augment the language.


I've been using python for many, many years.

One thing that always bugged me was the lack of "strict" mode (see perl)

For those of you not blessed enough to have done any serious perl, strict was a way of making sure that you hadn't accidentally assigned an output to a spelling mistake. In other words you had to declare all your variables _before_ you used them.

I got used to the lack of it, and grew to rely on pylint and other tools.

Then I went back to C++ (fuck that as ever) and started c#.

I really like c#, and I finally realised what I thought was missing from python: semi static typing.

Sure, you can use type hints, but they are pretty much useless as they have no runtime effect, so are basically highly schema'd comments.

What I would love is a mode in python where instead of duck typing everything, its statically typed, unless I do a magical incantation.

I know that will never happen, as its against python's core values. Its also entirely no trivial to re-build the interpreter. But it would mean we can stop type checking all the time & catch a fucktonne of bugs early.


> One thing that always bugged me was the lack of "strict" mode (see perl)

What Perl has as "strict" mode, Python just has by default all the time. The typing is dynamic but strong, it will complain about unbound variables instead of silently creating them, scoping, while not ideal, is sane out of the box.

What Python doesn't have is taint mode, but that is quirky and fragile as hell in Perl as well, and more of a nuisance nowadays than an actual aid.


> The typing is dynamic but strong, it will complain about unbound variables instead of silently creating them

Are you sure about that?

    def example(x):
      result = 123
      if x == 456:
        reslut = 789 # undetected typo
      return result
In Perl, this throws an error at compile time:

    use 5.036;
    sub example ($x) {
      my $result = 123;
      if ($x == 456) {
        $reslut = 789; # <-- Global symbol "$reslut" requires explicit package name
      }
      return $result;
    }


So what? This is just one class of the mistakes you can make. For the sake of argument, if you do have both "$result" and "$reslut" (they don't need to be named like this, of course, just continuing your example) with very different purposes, and you use the wrong one, the result is ... just as wrong.

I think all popular linters for Python catch the mistake you have shown since a decade or so. You create a variable and then don't use it.

On the other hand, I've been screwed over by wild list context suddenly appearing and swallowing my undefs (that I needed to be there) countless times. To this, Perl doesn't have a remedy. It won't even warn me that a function returned an undef but the list context squashed it into nothingness. This causes, for example, off-by-one errors that lead to creation of malformed hashes which sometimes may look almost right except the data is corrupted now. Even worse, two undefs instead of one will silence the warning about odd number of elements forming a hash, and that hash will be just as corrupted.

Perl weenies very much like to brag about crutches their language invented to bravely overcome the problems it itself has created, and it prevents them from realizing how limited those crutches are.


I'm not saying that Perl is perfect. Far from it.

But saying that Python "will complain about unbound variables instead of silently creating them" is simply incorrect. One of the defining characteristics of Python's variable scope is that assigning to a name always creates it in the current scope; there's no other way to declare a variable. And that's all I'm trying to demonstrate here.


The act of assigning is creating a variable; the act of using it will complain about an unbound variable.

Perl in non-strict mode, PHP last I touched it, Bourne-style shells will just go “yeah, whatever, here’s an empty string”.


perl is not a perfect language. nobody should be claiming that.

They way function args are define is just pure passive aggression (oh so you _want_ people to name args properly eh? go on, you validate the arg name. Go on, fiddle with the hash like that). However we should be able to look at any language and pull out its interesting/useful features.

I loved perl's can do attitude. You wanna do that to an array? sure. it'd going to end bad, but here you go kid, have at it.

Is that good for everyone? no.

does it mean that you need to be hilariously disciplined to make readable code? very yes.

Can one make readable and maintainable code in perl? hell yes. I've worked on some wonderfully maintainable perl code.

I've also worked on a lot of shite. But then the same goes for python. I transmute research code into production, and let me tell you, there are some "perls" of programmers right there.


yes! exactly this. Now obviously people would say "just use a linter" but its not actually as reliable as a builtin.

Moreover, people who have dyslexia are not always able to spot stuff like this.


Every python linter or code quality tool has been able to detect unused local variables like this since at least the year of our lord, 1841.

You don’t need this baked into the language.


Type hints aren't useless - you can type check your code before you run it (and they make your code more readable).

> What I would love is a mode in python where instead of duck typing everything, its statically typed, unless I do a magical incantation.

You can do this with mypy, you can set it to require types everywhere.


You get 99% of what you want by just using Python with Mypy (as a pre-commit hook, editor plugin, and/or CI step). Python has static type checking; it just comes in the form of Mypy instead of being baked into the Python executable.


So I work with a reasonably well typed codebase, type hints are great for documentation. Especially as most people where I work fear writing any kind reason _why_ they wrote what they wrote.

But it don't help with stuff that gets transmuted in flight. Granted with pydactic is your friend here, but thats a lot of work to backport.

basically I want c#'s typing system with python's syntax.


I enjoy writing statically type-checked Python (previously with Mypy, now Pyright) but it's always in my mind that in absolute terms this is an absurd situation that the language has worked itself into. Namely that all the effort you put into writing well-typed code, while beneficial for correctness thanks to said sidecar dev tooling, is thrown out the window and not used for optimisation by CPython.


JS and Python are simply bad languages that were simple and good enough to become popular. Now people are trying to fix them to meet needs they were not designed for.

It's a common trope: https://en.wikipedia.org/wiki/Worse_is_better


> Back in 2004 ... Java was the go-to for serious web application projects... In a way in some areas we are creating the new Java. We became the people we originally displaced.

Given that in 2023 the go-to language for serious server applications is still Java (at least by plurality), it is not at all surprising that languages that wished to displace it but didn't (or haven't) would eventually become more like it if they want to play in that domain.


The thoughts expressed in this post are filled with cognitive dissonance. The author should spend some time reconciling them - it would be very interesting to read an updated version of this post after the fact.


(Meta comment alert) people are still a bit too jazzed about type safety. If you are having a strong reaction without (having been forced to) using typing in Python, please try to have a softer stance, in this case. It really kinda sucks.

Also another comment about the apparent contradictions about moving quickly without types and then dealing with runtime errors. I don’t think the author actually said anything about the nature of the stack trace, and I’m pretty sure lots of typed languages have runtime errors that require combing through stack traces. The point of removing the guardrails is that it allows you to shape the clay that is your code, iteratively, without thinking too hard about the correctness. “Yes it isn’t right, but it’s good enough for 70% and I’ll figure out the rest after I prove this out.” When you have “grand” ideas you want to be able to get it out quickly, and not be slowed down by proof. Is it really that hard to keep those two ideas beside each other?


Sure, for simple scripts, math, and research, types aren't needed and dynamic typing is excellent to have. But when you have complex code that was normally written in C++ and Java in Python, where it's more concrete and mechanical than abstract, type checking eliminates an entire category of errors.


" I recall directly modifying live websites via FTP in real time. Later editing web sites straight from vim on the production server. "

I wonder how much money was at stake from these edits?


I feel obligated to mention that large projects occurred in Python before types (and were not spaghetti). But I cut my teeth in the 2.4 era with google docstrings which obligated the author to put the types in the docstring.

Sorry for another hot take. I definitely have sympathies toward the author's position, we are giving up flexibility which is Python's main strength for what exactly? (Might as well write Go/etc if you want static types)

- the good: as a reader I love not having to go to call site after call site figuring out _what type_ of thing was passed in and how that happened (google docstring projects don't have this problem but open source often does)

- the bad: as a writer the types are complicated and obtuse, and the more fancy first order stuff you do the uglier the types get (and just forget type annotating your unit tests). Also mypy is slow

- the ugly: the sadness of putting in all of the typing and it _not_ but used to improve runtime performance. Commonlisp gets something like a 30x speed improvement for type annotations because native code can be used in place of lisp objects, why can't we have _that_? I would feel much better about Python types if we did that


> Here is what has changed: we no longer trust developers as much and we are re-introducing the complexity that we were fighting.

What I think has changed instead: Python is used for large code bases, unlike 15 years ago, and without type annotations it is an unreadable mess.


I do agree that TypeScript is the far better after-the-fact addition of types to a previously untyped dynamic language. But if you’re stuck using Python that won’t mean much to you.


Author could get the best of both worlds: enjoy the typing in third party libraries for IntelliSense, and not use typing at all in own code.

So what exactly is the complaint?


Fake typing in python was a terrible idea I can't believe got merged in. The initial version where you had to import new typing classes was particularly ugly. Now it is mitigated somewhat with as you would expect builtin type hints.

The worst thing for me is I have to work with javascript devs that can also do python and they incessantly use python type hinting because they come from typescript.

It's useless, clutters up the code and decreases readability.


Funny. I have the exact opposite experience. Type hints tell me what the code actually does and what kind of arguments functions and methods actually expect.

It's all sunshine and rainbows if you work with well documented libraries, but there are tons of python libs out there that just use *kwargs as their function signatures with maybe - if you're lucky - a list of actual keywords (no types or description though).

Those are hell to work with, because the IDE can't tell you what's going on. So you either have to delve into sample code (and hope that's still up-to-date) or look at the source code to figure out what the heck is going on.

So no, I personally don't find type hints useless at all - especially for libraries. Readability OTOH is subjective and mostly just a matter of what you're used to, anyway.


> It's useless, clutters up the code and decreases readability.

I submit that dataclasses refute these claims handily.


I am tired of Armin's opinions and hot takes. I appreciate the work he has done but we are changing (maturing?) as a community. We have learned that there is a real cost to dynamic languages and there is not always a benefit to them.

To people that like what they see in this post please try Go if you are on a team. TypeScripts attracts typing astronauts that can and will turn a codebase into and unmaintainable mess just the same as untyped Python.

Of course you might not value team velocity over your own velocity and that, is indeed, a different conversation.

I have said for years: Go makes teams effective the same way Python makes individual devs effective.

Separately:

> Companies like Microsoft did not even think that Python was a language yet

So tired of this trope. Microsoft shipped Python in 1996[0] AND they invested in the ecosystem by offering IronPython[1].

0: http://python-history.blogspot.com/2009/01/microsoft-ships-p...

1: https://en.m.wikipedia.org/wiki/IronPython




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: