| ▲ | tasn 10 hours ago |
| The Python type system is pretty bad, but it's still 100x better than not using types. We are heavy users of the (Rust) type system at Svix, and it's been a godsend. I wrote about it here https://www.svix.com/blog/strong-typing-hill-to-die-on/ We also use Python in some places, including the shitty Python type-system (and some cool hackery to make SQLAlchemy feel very typed and work nicely with Pydantic). |
|
| ▲ | belorn 7 hours ago | parent | next [-] |
| Looking at that blog post, I find it illustrative in how people who like strong types and people who dislike strong types are addressing different form of bugs. If the main types of issues comes from bugs like 1 + "2" == 12", then strong types is a big help. It also enables many developers who spend the majority of time in a programming editor to quickly get automatic help with such bugs. The other side is those people who do not find those kind of bugs annoying, or they simply don't get hit by such bugs at a rate that is high enough to warrant using a strong type system. Developers who spend their time prototyping in ipython also get less out of the strong types. The bugs that those developers are concerned about are design bugs, like finding out why a bunch of small async programs reading from a message buss may stall once every second Friday, and where the bug may be a dependency of a dependency of a dependency that do not use a socket timeout. Types are similar not going to help those who spend the wast majority of time on bugs where someone finally says "This design could never have worked". |
| |
| ▲ | teddyh 3 hours ago | parent | next [-] | | Take care to differentiate strong/weak typing from dynamic/static typing. Many dynamically typed languages (especially older ones) are also weakly typed, but some dynamic langugages, like Python, are strongly typed. 1 + "2" == 12 is weak typing, and Python has strong typing. Type declarations are static typing, in contrast to traditional Python, which had (and still has) dynamic typing. | |
| ▲ | satvikpendem 3 hours ago | parent | prev | next [-] | | It's not about the bugs, it's about designing the layout of the program in types first (ie, laying out all of the data structures required) such that the actual coding of the functionality is fairly trivial. This is known as type driven development: https://blog.ploeh.dk/2015/08/10/type-driven-development/ | | |
| ▲ | slowking2 35 minutes ago | parent [-] | | At work, I find type hints useful as basically enforced documentation and as a weak sort of test, but few type systems offer decent basic support for the sort of things you would need to do type driven programming in scientific/numerical work. Things like making sure matrices have compatible dimensions, handling units, and constraining the range of a numerical variable would be a solid minimum. I've read that F# has units, Ada and Pascal have ranges as types (my understanding is these are runtime enforced mostly), Rust will land const generics that might be useful for matrix type stuff some time soon. Does any language support all 3 of these things well together? Do you basically need fully dependent types for this? Obviously, with discipline you can work to enforce all these things at runtime, but I'd like it if there was a language that made all 3 of these things straightforward. |
| |
| ▲ | hbrn 2 hours ago | parent | prev | next [-] | | What's even worse, when typing is treated as an indisputable virtue (and not a tradeoff), pretty much every team starts sacrificing readability for the sake of typing. And lo and behold, they end up with _more_ design bugs. And the sad part is that they will never even recognize that too much typing is to blame. | | |
| ▲ | IshKebab an hour ago | parent [-] | | Nonsense. You might consider it a tradeoff, but it's a very heavily skewed one. Minor downsides on one side, huge upsides on the other. Also I would say type hints sacrifice aesthetics, not readability. Most code with type hints is easier to read, in the same way that graphs with labelled axes and units are easier to read. They might have more "stuff" there which people might think is ugly, but they convey critical information which allows you to understand the code. | | |
| ▲ | hbrn an hour ago | parent [-] | | > Most code with type hints is easier to read That has not been my experience in the past few years. I've always been a fan of type hints in Python: intention behind them was to contribute to readability and when developer had that intention in mind, they worked really well. However, with the release of mypy and Typescript, engineering culture largely shifted towards "typing is a virtue" mindset. Type hints are no longer a documentation tool, they are a constraint enforcing tool. And that tool is often at odds with readability. Readability is subjective and ephemeral, type constraints (and intellisense) are very tangible. Naturally, developers are failing to find balance between the two. |
|
| |
| ▲ | tasn 4 hours ago | parent | prev | next [-] | | I think you're missing the point of the blog a bit, as the `1 + "2" == "12"` type of issues wasn't it. It definitely also sucks and much more common than you make it sound (especially when refactoring) but it's definitely not that. Anyhow, no need to rehash the same arguments, there was a long thread here on HN about the post, you can read some of it here: https://news.ycombinator.com/item?id=37764326 | |
| ▲ | rty32 2 hours ago | parent | prev [-] | | > The other side is those people who do not find those kind of bugs annoying Anecdotally, I find these are the same people who work less effectively and efficiently. At my company, I know people who mainly use Notepad++ for editing code when VSCode (or another IDE) is readily available, who use print over debuggers, who don't get frustrated by runtime errors that could be caught in IDEs, and who opt out of using coding assistants. I happen to know as a matter of fact that the person who codes in Notepad++ frequently has trivial errors, and generally these people don't push code out as fast they could. And they don't care to change the way they work even after seeing the alternatives and knowing they are objectively more efficient. I am not their managers, so I say to myself "this is none of my business" and move on. I do feel pity for them. | | |
| ▲ | lblume an hour ago | parent | next [-] | | Well, using print over debuggers is fairly common in Rust and other languages with strong type systems because most bugs are, due to the extreme lengths the compiler goes to to able to detect them even before running the program, just lacks of information of the value of an expression at a single point in the program flow, which is where dbg! shines. I agree with all the other points though. Anecdotally, I was just writing a generic BPE implementation, and spend a few hours tracking down a bug. I used debug statements to look at the values of expressions, and noticed that something was off. Only later did I figure out that I modified a value, but used the old copy — a simple logic error that #[must_use] could have prevented. cargo clippy -W pedantic is annoying, but this taught be I better listen to what it has to say. | |
| ▲ | otherme123 an hour ago | parent | prev [-] | | >these people don't push code out as fast they could. Well, one of my coworkers pushes code quite fast, and also he is the one who get rejected more often because he keep adding .tmp, .pyc and even .env files to his commits. I guess "git add asterisk" is faster, and thus more efficient, than adding files slowly or taking time to edit gitignore. Not so long ago I read a history here in HN about a guy that first coded in his head, then wrote everything in paper, and finally coded in a computer. It compiled without errors. Slow pusher? Inefficient? |
|
|
|
| ▲ | junyjujfjf 27 minutes ago | parent | prev | next [-] |
| > The Python type system is pretty bad Coming from the perspective of a religious python hater, their type hints are better than what you give credit for: Supports generics, nominative, structural, unions, bottom type, and literals. What is missing is mainstream adoption in libraries which is a matter of time. |
| |
| ▲ | maleldil 11 minutes ago | parent [-] | | > What is missing is mainstream adoption in libraries which is a matter of time. I don't think that's a big problem anymore. Between typeshed and typing's overall momentum, most libraries have at least decent typing and those that don't often have typed alternatives. |
|
|
| ▲ | hbrn 2 hours ago | parent | prev | next [-] |
| > Writing software without types lets you go at full speed. Full speed towards the cliff. Isn't it strange that back when Python (or Ruby) didn't even have type hints (not type checkers, type hints!), it would easily outperform pretty much every heavily typed language? Somehow when types weren't an option we weren't going towards the cliff, but now that they are, not using them means jumping off a cliff? Something doesn't add up. |
| |
| ▲ | dinosaurdynasty an hour ago | parent | next [-] | | It's because the nature of typing has changed drastically over the last decade or so, in well known languages, going from C++/Java's `FancyObject *fancyObject = new FancyObject()` (which was definitely annoying to type, and was seen as a way to "tell the compiler how to arrange memory" as opposed to "how do we ensure constraints hold?") to modern TypeScript, where large well-typed programs can be written with barely a type annotation in sight. There's also a larger understanding that as programs get larger and larger, they get harder to maintain and more importantly refactor, and good types help with this much more than brittle unit tests do. (You can also eliminate a lot of busywork tests with types.) | | |
| ▲ | hbrn 27 minutes ago | parent [-] | | Large programs are harder to maintain because people don't have the balls to break them into smaller ones with proper boundaries. They prefer incremental bandaids like type hints or unit tests that make it easier to deal with the big ball of mud, instead of not building the ball in the first place. |
| |
| ▲ | IshKebab an hour ago | parent | prev [-] | | > when types weren't an option we weren't going towards the cliff Erm yes we were. Untyped Python wasn't magically tolerable just because type hints hadn't been implemented yet. | | |
|
|
| ▲ | VBprogrammer 2 hours ago | parent | prev | next [-] |
| I had a play with Dart a while back. It felt like Python with types designed in from the outset. Would quite like to use it more seriously. It's in that funny position though where it is in danger of becoming synonymous with Flutter. Like Ruby and Rails. |
|
| ▲ | youdontknowjuli 10 hours ago | parent | prev | next [-] |
| > (and some cool hackery to make SQLAlchemy feel very typed and work nicely with Pydantic). Sounds interesting. Can you elaborate on the cool hackery? We introduced SQLModel recently but struggle in a few cases (e.g. multi-level joins). Do you know reference projects for SQLAlchemy and pydantic? |
| |
| ▲ | tasn 4 hours ago | parent [-] | | My info is maybe a bit dated, as it's been a while since we wrote this hackery. We also adopted SQLModel at some point but we had to patch it to work well (I think some of my contributions are now in upstream). As for some of the hacks: def c(prop: t.Any) -> sa.Column: # type: ignore
return prop
To make it possible to access sqlmodel properties as columns for doing things like `in_` but still maintaining type safety.Added types ourselves to the base model like this: __table__: t.ClassVar[sa.Table]
Added functions that help with typing like this: @classmethod
async def _fetch_one(cls: t.Type[BaseT], db: BaseReadOnlySqlSession, query: Select) -> t.Optional[BaseT]:
try:
return (await db.execute(query)).scalar_one()
except NoResultFound:
return None
and stuff like this for relationships: def ezrelationship(
model: t.Type[T_],
id_our: t.Union[str, sa.Column], # type: ignore
id_other: t.Optional[t.Union[t.Any, sa.Column]] = None, # type: ignore
) -> T_:
if id_other is None:
id_other = model.id
return sqlm.Relationship(sa_relationship=relationship(model, primaryjoin=f"foreign({id_our}) == {id_other}"))
def ezrelationship_back(
id_our: t.Union[str, sa.Column], # type: ignore
id_other: t.Union[str, sa.Column], # type: ignore
) -> t.Any:
model, only_id2 = id_other.split(".")
return sqlm.Relationship(
sa_relationship=relationship(
model,
primaryjoin=f"foreign({id_our}) == {id_other}_id",
back_populates=only_id2,
)
)
I hope this helps, I don't have time to find all the stuff, but we also hacked on SQLAlchemy a bit, and in other places. |
|
|
| ▲ | ansgri 7 hours ago | parent | prev | next [-] |
| If it’s 100x better than no types, then probably 10x better than C++ type system. It takes some time to unlearn using dicts everywhere, but then namedtuples become your best friend and noticeably improve maintainability. Probably the only place where python type system feels inadequate is describing json-like data near the point of its (de)serialization. |
| |
| ▲ | tecoholic 2 hours ago | parent | next [-] | | There’s TyepdDict that is decent for a JSON like data structure if the types are simple. It doesn’t have the bells and whistles of Pydantic, but gets the job done for passing predictable dicts around and ensuring consistency while developing | |
| ▲ | rcfox 2 hours ago | parent | prev [-] | | Pretty much anywhere you're tempted to use a namedtuple, you should be using a dataclass[0] instead. And typing JSON-like data is possible with TypedDict[1]. [0] https://docs.python.org/3/library/dataclasses.html [1] https://docs.python.org/3/library/typing.html#typing.TypedDi... | | |
| ▲ | ansgri 2 hours ago | parent [-] | | Why? I thought one should prefer immutability. As for typed dicts.. yes, I’m mostly stuck on old python versions, nice reminder. | | |
| ▲ | maleldil 26 minutes ago | parent [-] | | You can use TypedDict from `typing_extensions` if your version doesn't have it. You can use a lot of the newer stuff from there, too, especially if you enable `__future__.annotations`. How old is your Python, though? TypedDict is from 3.8. That was 5 years ago. |
|
|
|
|
| ▲ | Myrmornis 10 hours ago | parent | prev [-] |
| Can you give some examples of how the Python type system is disappointing you? |
| |
| ▲ | CJefferson 2 hours ago | parent | next [-] | | Mainly, the seems to be no way, in a dynamic language, to dynamically check if functions get the right types. To me, this means I don't really understand the python type hinting at all, as adding hints to just one or two functions provides no value to me at all. I assume I must be not using them usefully, as I've tried adding type hints to some projects and they just seemed to do nothing useful. | | | |
| ▲ | colemannerd 3 hours ago | parent | prev [-] | | default values! Since type hints are *hints*, it is difficult to set default values for complicated types. For instance, if you have lists, dicts, sets in the type signature, without a library like pydantic, it is difficult and non-standard. This becomes even more problematic when you start doing more complicated data structures. The configuration in this library starts to show the problems. https://koxudaxi.github.io/datamodel-code-generator/custom_t... The issue very much is a lack of a standard for the entire language; rather than it not being possible. | | |
| ▲ | alfons_foobar 2 hours ago | parent [-] | | I might be dense, but I don't understand what that has to do with type hints... To my eyes, the problem of choosing useful defaults for complicated types/datastructures is independent of whether I add type hints for them. I think I am missing something... |
|
|