12 ms·
Writing Python like Rust (2020)
- voz_ 3y agoType annotations do not rust make. And lack of types does not Python make. As someone who has learned to love both languages as tools for the job, I want to write Python like Python and rust like rust.
- bbkane 3y agoI want to write correct and maintainable code, and techniques like these make that easier for me. I've found that if it's hard to type hint my code, there's probably a better way to design it.
- ddejohn 3y agoI find that approaching tasks from the type-first perspective can really help illuminate the problem. I can't imagine trying to code without having both types at the forefront of my mind and all of the IDE's helpful type-related features.
- fbdab103 3y agoSimilar to why I like unit-testing. Writing code in a testable way usually straightens out what would have been a thornier first-pass interface. Even if the test never fails, it made me be explicit about my assumptions.
- deepsun 3y agoThere's little value in maintainable code when the task at hand is to quickly run some statistics and drop it right away. Similar to a Bash script, or, to a lesser extent -- R. But the speed of writing is paramount.
- tru1ock 3y agoSoftware development has never been about building correct systems that you see so many here care about. There might be a few contexts where that is the main value being optimized for, but the vast majority of other it is not. I think a lot of smart people are in the wrong industry and it causes them pain. I see so many overworked and stressed out people and it is almost always internal reality causing it. http://www.laputan.org/mud/ http://www.laputan.org/mud/
- ghnws 3y ago> Type annotations do not rust make. And lack of types does not Python make. That is never implied. See the first example in the article.
- osigurdson 3y agoWhile I respect people that prefer programming without explicit types, I can’t understand it.
- tnecniv 3y agoThis was something that was in vogue in the mid / late 2000s, then Haskell became the cool kid language and types are back. The thing that drives me crazy about Python typing is that I need to import stuff to make full use of the type system. Like I get that the typing is option because that would be a huge language overhaul, but it feels weird that so many tools get hidden inside libraries (standing or otherwise).
- deleted 3y ago[deleted]
- scubbo 3y agoAFAIK, you only need to import the actual types that you're using (`from typing import List, Iterable, Optional, ...`). What else is there?
- bbkane 3y agoIn newer Python versions, you don't need most of those imports either: foo: list[str] = ["a"] bar: int | None = 1
- saulpw 3y agoYeah, but that's completely unnecessary friction.
- scubbo 3y agoGranted - but the original claim was that "it feels weird that so many tools get hidden inside libraries", and I'm asking for an example of more than one of these "so many".
- abdullahkhalids 3y ago
- ddejohn 3y agoMy current role is at a company with a few hundred thousand lines of un-typed, undocumented, mostly un-tested Python. I get a small allowance of "tech debt busting" time, and it's absurd how many bugs I find just by trying to suss out the types on a given code path. It's maddening work.
- l0b0 3y agoBringing a non-trivial code base up to even half-decent standards can be a monumental task, and because such code bases are inherently overly complex and risky to change there's only so much you can do without breaking production constantly. It's a lose-lose proposition, which is why I'd never want to be "the programmer" at some place like that, no matter the pay.
- synergy20 3y agodjango has 500K lines of python and it's considered well written and stable, so it's possible to do python with large code base I think. oddly, django does not seem to be even using type hints, new features such as dataclasses was not found from the source base either, still it just works well somehow.
- dsissitka 3y agoI was curious: SLOC Directory SLOC-by-Language (Sorted) 232099 tests python=232099 102470 django python=102470 424 docs python=424 158 scripts python=142,sh=16 21 top_dir python=21 0 extras (none) 0 js_tests (none) Totals grouped by language (dominant language first): python: 335156 (100.00%) sh: 16 (0.00%) Please credit this data as "generated using David A. Wheeler's 'SLOCCount'."
- deepsun 3y agoWow, 2.3x for tests/src ratio. I remember some recommendation for Java to have around 0.9x ratio, otherwise too much tests maintenance. Depends on the project of course, e.g. SQLite has an insane ratio.
- woodruffw 3y ago(2020). Things have arguably become even nicer (although slightly more divergent between the two) since then: Python's `Optional[T]` can now be written as `T | None`, and the core container types can now be annotated directly (e.g. `List[T]` becomes `list[T]`). Combined via pyO3[1], Python and Rust are a real joy to write together. [1]: https://github.com/PyO3/pyo3 https://github.com/PyO3/pyo3
- tayo42 3y agoDidn't notice the date, I was confused about the comment about pattern matching
- diarrhea 3y agoI don’t think the new way to write None is an improvement in this case. I use the pipe as a replacement for Union (of which Optional is a special case, but oddly, there is prefer to use Optional; it reads nicely).
- kzrdude 3y agoI'm not that deep into type annotations in Python, but I'm not a fan of type-only structures, or mixing concrete with type aliases. That is, `list[T]` is a concrete list, but `Optional[T]` is either a plain T or a plain None. There is no class called Optional.
- rirze 3y agoI prefer the new way, to me it's explicit about the type of Object that can be passed in.
- andrewqu 3y agoI'd rather write rust like python
- jjtheblunt 3y agoIm hoping Mojo supports essentially that.
- jasfi 3y agoSounds like Nim.
- oldelpaso66 3y agoThere was another one of these posts floating around recently https://kobzol.github.io/rust/python/2023/05/20/writing-python-like-its-rust.html https://kobzol.github.io/rust/python/2023/05/20/writing-pyth... My personal experience is that while this would be a really good way of getting people to write quality Python code, a lot of python users (data scientists) are not software engineers and don't have the discipline to do this without very strict PR reviews
- guggle 3y agoConvincing my colleagues to use type hints in Python has been very easy. The benefits are obvious and immediate. To the point where I wonder why there was even a time when it was acceptable not to have them.
- d--b 3y ago> At this point, you’re maybe wondering why we didn’t just write the project in Rust? Err, aren’t Python and Rust designed to write completely different kinds of programs? Python => scripting, data stuff, unsafe and slow, quick’n’dirty PoCs Rust => compiled, system stuff, robust and fast, production work. If the author is balancing the 2, they probably should have used C# !
- jeremycarter 3y agoC# really is an absolute gem! Don't know why more people don't give it a try.
- jug6ernaut 3y agoDesigned with different goals absolutely, they have very different use cases that they individually accel at. This however does not stop people from trying to use their one preferred language fer beyond it's focus area. IMO one of the major turning points in a developers career is when they learn that finding the right tool for the problem is always the game you are playing. But this almost always takes learning this lesson the hard way by pushing a language one know very well past its breakpoint.
- berkes 3y agoI'm there. I write Ruby, JavaScript, Python, Java, Bash, and Rust for work. Rust is surprisingly good at many tasks I would otherwise pick "the proper" tool for. A web service, a quick frontend in wasm (leptos), a hackish commandline tool, or, indeed, that low-level/embedded thing that Rust was "meant" for.
- maleldil 3y agoHow is Python 'unsafe'?
- c_crank 3y ago[flagged]
- jgilias 3y ago> Of course, if you’re the sort of person who deals with errors in rust by just slapping ? on anything that might fail, then you’re more or less just doing exceptions Well, no, not really. You typically still explicitly enumerate how your code may fail to use that notation (unless it’s a small utility where you just anyhow it), you also have the fallible values clearly denoted as such, and not everything is fallible. And last, but not least, every time I slap a ‘?’ I tend to quickly evaluate what the failure case is, and if it’s something I can’t do anything about at the moment and should just bubble up, or is it something I can actually handle. So, no, not the same thing as exceptions at all!
- Zamiel_Snawley 3y agoAnd also, returning errors by value does not introduce hidden control flow! In python, you have no way of knowing what(if any) exceptions that a function may throw, so unless you catch everything you have no way of knowing where execution will end up after calling a function. Not to mention the intentionally(!) thrown exceptions for iterators. In my book, exceptions are goto by another name.
- diarrhea 3y agoExceptions are goto in disguise to some extent, as it’s forward-jumping only. An important limitation to retain structured programming in this case.
- dumdumchan 3y agoI could show you 20 functions in stdlib itself and popular libraries that return an error but don't mention under what circumstances. They have an internal error type that is a catchall and this isn't exactly an upgrade over exceptions.
- dataking 3y ago> Unlike in a strongly typed language, the python type annotations aren’t enforced. Python is a strongly typed language but type checks happen at runtime rather than ahead of time [0]. The author is correct that the type annotations aren't enforced. [0] https://wiki.python.org/moin/Why%20is%20Python%20a%20dynamic%20language%20and%20also%20a%20strongly%20typed%20language https://wiki.python.org/moin/Why%20is%20Python%20a%20dynamic...
- deleted 3y ago[deleted]
- tgv 3y agoI'm not taking those arguments seriously anymore. Here's from the linked doc: > In a weakly typed language a compiler / interpreter will sometimes change the type of a variable Well, a = 1 a = "a" a = [1, a] executes without any problem, so according to that argument, Python's typing is weak. Even if you take the "dynamic but strong" argument, it only holds for a few specific operations. A function like this cannot be considered strongly typed: def f(x, y): return x + y because I can call f(1, 2) or f("a", "b") without any problem. People have been desperately wanting to claim that Python has strong typing, but it not having weak typing doesn't make it strong. They're not binary opposites.
- DarkNova6 3y agoI would phrase it like this: Not only is Python dynamically typed, it is strongly dynamically typed.
- uv-depression 3y agoI don't think there are generally agreed upon definitions of strong vs. weak, but I think a useful definition is about changing the type _while referring to the same data_ as weakness. In your Python example there, I think we can say it's dynamically + strongly typed because you're assigning the name `a` to three different objects with different types, not changing the interpretation of any actual data in memory. C would be statically + weakly typed, because you can cast the same exact data to whatever type by using `void*`. Rust is static + strong, and I can't really think of a dynamic + weak language. Probably that would be a nightmare to work in.
- laurencerowe 3y ago> What I like about the Rust (and to a lesser extent, Go) approach to error handling is, you know up front if and what errors a function might return. And additionally, the compiler forces you to at least consider how you’re going to deal with it. > By comparison, the python approach to dealing with exceptions is quite chunky > try: > function() > except SomeException: > # etc > I think this, in fact, encourages just letting exceptions bubble up, rather than fussing about with handling them. Letting exceptions bubble up is usually the right thing to do. Handle them centrally, e.g. in middleware for a web application.
- mindwok 3y agoAgree. I spent quite a lot of effort at a previous role trying to convince junior developers to stop using except pretty much everywhere and just let the program explode, because that's really what you want most of the time. The problem is that sometimes in Python exceptions can be used for control flow, or for things that are reasonably common. In that case, it's very useful to know ahead of time what errors might be returned.
- dotancohen 3y ago> The problem is that sometimes in Python exceptions can be used for control flow, or for things that are reasonably common. Almost every time I've heard this argument, I've disagreed with it. I think that the only time using Exceptions for flow control was valid was some string-validation technique (like ensuring that a string was JSON or an email address) that could only throw an exception on error. I'd love to see _your_ use case, though.
- stevesimmons 3y ago> The problem is that sometimes in Python exceptions can be used for control flow, or for things that are reasonably common. Using exceptions for control flow is, imo, fine within a single function (e.g. for x in y: try: blah except KeyError: continue). Using exceptions for control flow across function boundaries seems a recipe for confusion, unless very clearly documented.
- dehrmann 3y agoVery similar article a month ago: https://news.ycombinator.com/item?id=36018621 https://news.ycombinator.com/item?id=36018621
- usrbinbash 3y agoType hints are an absolute must for me in modern Python, starting with one really simple reason: All the tooling can use them. That means: - The static checker / linter - My LSP server - Documentation Generators - Testing frameworks All of them can use type hints to work better, or to put this in to words that interest me: make my life easier. Laziness is a programmers virtue. I am lazy. I don't want to guess or lookup what type a param has. I want my LSP to yell it at me loudly when calling something. Taking the 1.5 extra seconds to write that type hint, is a minute saved later when using the code. Oh, and to everyone using LLM in their editing environment: They understand type hints as well, and can make good use of them. And for when I am feeling especially lazy: I have a keybind that sends the current code block to the LLM asking it to add type hints and a docstring ;-)
- jpc0 3y agoThere is also improving support with tools like cython.
- domust 3y ago> Of course, one advantage Python has over Rust in this case is being able to give fields default values. https://doc.rust-lang.org/std/default/trait.Default.html https://doc.rust-lang.org/std/default/trait.Default.html
- Pannoniae 3y ago"In spite of the topic of this blog, I think you should always aim to write code which is idiomatic to the language you’re using. If only so it’s accessible to whoever has to maintain your code after you’re gone." Come on. This approach is almost always used to justify shitty practices just because they are idiomatic (read: the language has a severe limitation in the area) This is a really good example - if you can use a library with proper option types or other constructs which reduce bugs - go ahead! Similar with C# and libraries like language-ext - zero idiomacity but in some cases, can replace hundreds of lines of code while preventing obvious bugs. Whoever has to maintain it afterwards should be grateful about being exposed to different styles and language idioms instead of complaining that he's been taken out of his comfort zone, in my opinion.
- cornholio 3y agoConversely, your approach has been used to justify the most abhorrent programming practices even perpetrated on mankind. What you get are not carefully chosen abstractions, but custom mini-languages with no specification masquerading as the original language by redefining basic primitives, a dependency hell to arbitrary and non maintained weekend projects someone dumped on github, which would have been easy to write in terms of the standard library - if only the original author knew its capabilities - but now they must be reverse-engineered from context etc. The maintainer is not ready for a paradigm shift because they will not own your program in perpetuity. Even you yourself ran away and abandoned it, so how can you expect someone else to love it more? No, the maintainer has a limited time and attention span to work on a specific bug or feature, and your code is either maintainable or it's not.
- Pannoniae 3y agoI think you assume that the choice is between "good, idiomatic code" and "non-idiomatic, horrible, abandoned mess". Most of what you said are problems with simply not documenting code and writing code well enough; being idiomatic slightly helps in that by making it more familiar, but that's just a masking of the fact that the program is written horribly with no documentation. Libraries like language-ext are absolutely not "badly chosen abstractions" or "dependency hell". Also, "your code is either maintainable or it's not." is heavily subjective. Maintainability is often also used to criticise stylistic choices like variable naming, tabs vs. spaces, brace placement and other things which might be non-standard but don't detract from readability at all. If it did, there wouldn't be so many different standards across languages.
- p4bl0 3y agoA few weeks ago I tweeted this [1]: Frankly, writing Python code in 2023 with types annotations, dataclasses, structural pattern matching, and an editor (Kate ♥) that supports LSP with the mypy plugin for pylsp installed is just as cool as writing OCaml code! The addition of the `match` construct since Python 3.10 is indeed a huge additional improvement that is called for in the linked post which dates back from before it was a thing (from the blogpost: “One thing I miss from Rust is its ‘richer’ enums, and pattern matching”). [1], in french https://twitter.com/p4bl0/status/1667300422426476544 https://twitter.com/p4bl0/status/1667300422426476544
- tipsytoad 3y agoI didn't know about Protocol! How does this differ from ABC?
- hmry 3y agoYou need to explicitly inherit from an ABC to be considered an instance of it. With Protocols you just need to match the interface, duck-typing style. (You can also do this with ABCs by writing custom __subclasscheck__ and __instancecheck__ functions, but those can't be statically checked, they only work at runtime) AFAIK protocols actually use ABCs internally(?) Just in a more constrained form that static type checkers can understand.
- crabbone 3y agoWasn't this posted here less than a month ago? OP has too much free time on their hands and is doing something worthless, but that somehow attracts a lot of "me too" comments. Guess this only indicates how bad the average professional or an aspiring professional is in this field...
- bionhoward 3y agoNewType and TypeGuard are pretty close to a trait. I really want this approach to be awesome but it’s actually ironically seemingly more work to write python like rust than to just write rust. I say this because if you write python like rust then you have to re-invent and unit test and e2e test a bunch of stuff you get for free in rust. I do think Immutables.Map + NewType + TypeGuard is enough for some awesome category theory stuff, but it’s a bit harder with Pydantic if you want to handle nested stuff (I guess I just need to make more data classes) and then another issue is you have to write custom code to check if a thing implements a given trait. Then you have to use that. Making sure all of that basic stuff you get for free in rust works correctly in your Python implementation is a bit of work! Worth it? unclear, still testing, I do love me some duck typing!
- c_crank 3y agoTrying to write Objects in Python is already bad enough, I can't imagine trying to force any other kind of complex crap through that language. If you want something that isn't Python, your best bet is not using Python.