Hacker News new | ask | show | jobs
by tmpz22 1065 days ago
It’s disappointing that a lot of arguments for typing fail to explore why untyped languages became popular to begin with and how those benefits can be maintained throughout the migration process through partial typing To complete typing.

When typing is applied gradually throughout a codebase it can sometimes manifest as the worst of both world and a complete argument should at least acknowledge that as well as the impact of bad typing on codebases.

3 comments

What are the downsides of gradual typing on a codebase? I've never worked with it, but watched some talks on it and it really sounds promising. I understand the downsides for compiler complexity and for performance, but not on the codebase itself.
Some downsides of gradual typing:

- Worse type error messages, as the type system has to be more complicated to handle the sorts of patterns that are common in untyped code.

- You can still get type errors at runtime in your typed code, if it interacts with untyped code, because it isn't feasible to validate all types on the boundary. You're only guaranteed no runtime type errors if all of your code is typed.

- The migration path from untyped to typed isn't always easy. Depending on how your code is organized, it's possible to have correct untyped code such that there do not exist type annotations you could add to it that would make it type check.

For one thing: because as soon as some piece of data leaves the typed world, all of your guarantees about its integrity go away. This is, in some sense, even worse than a situation with no types at all - in that case, you have no guarantees, which is better than having misleading ones!

If types are to data flow what structured programming is to control flow, it's a bit like being able to call into unsafe functions that can corrupt your stack. You could waste hours trying to trace the flow of execution and being completely stumped by your code's behaviour, looking at stack traces that make no sense...

Furthering that analogy, like how a structured programming language restricts control flow to specific structures as compared to free-form branches and jumps, certain interfaces and patterns that you can get away with in a dynamic context can't be expressed (or expressed cleanly) in a statically typed language. Just as your average Java programmer doesn't usually feel constricted by the inability to write Duff's Device in their language of choice, once you get good at TypeScript you rarely feel inclined to construct dynamic interfaces, but JS developers...a world where static typing isn't universally enforced, especially if you're working with a bunch of JS devs who don't fully embrace static typing, encourages the construction of difficult-to-type APIs. This then feeds back into the first issue: reasoning about type integrity when using these APIs becomes painfully difficult.

It's not that the general idea of "gradual typing" is bad, but that strong types become a leaky abstraction in practice. If you have to constantly fight against the weakly typed nature of the underlying language, that defeats the point.

Here's a writeup on it. https://www.onux.com/jspp/tutorials/type-system#Appendix-B

> The problem is not as simple as taking the ECMAScript grammar and augmenting it with type annotations. There's a reason that Microsoft (TypeScript, Safe TypeScript), Google (AtScript, SoundScript), and Facebook (Flow) have all collectively attempted this problem and came up short.

It continues for a while...

Types are ultimately a contract. If your code was not originally designed with these contracts in mind and then you introduce a system that actually promotes ad hoc interfaces (TypeScript or any structurally typed system) then what you get is a mess. You're constantly Pick or Omit-ting everywhere. But worse, you absolutely have to use "any" or "ts-ignore" during the process. You can't avoid it. But one single "any" has the effect of stripping all code of type annotations, making the entire process a giant waste of time.

To compound matters, none of the TypeScript devs I've worked with have any experience with type systems. They don't think in terms of interfaces. They are still ball-of-mud developers. They did not come from Java or C/C++ or even Haskell. They came from Python or Ruby.

I'd also caution people about using third party types (i.e. DefinitelyTyped). These are often not correct because the underlying library is not in TS and does not have type information. You will pull your hair out when the library does not match the type definitions. These third party definitions can introduce bugs into your code. They can falsely claim that some field exists on an object which, in fact, does not. Your IDE will happily autocomplete to the invalid field, TS will happily compile it, and your JS run-time will unhappily crash.

Done badly you can end up doing a lot of admin to make the types work without actually getting the benefit of that type safety. Done well you can get the benefits of type safety where it really counts and avoid the overhead where it doesn’t.
> What are the downsides of gradual typing on a codebase?

Typing complex business domains and interactions is hard. Most developers don’t have a lot of experience with it or the time to do it properly as they produce features. It doesn’t help that many developers start their career with untyped languages and transition into typed languages without learning it properly.

Typescript is a great example because a lot of frontend developers may have only ever worked in JavaScript and have absolutely no foundation to build on but trial and error on your companies production codebase.

It's definitely not that the developers are inexperienced with types. You're sort of making a "white man's burden" argument. Very few web developers are just writing javascript at work.

It's simply that you can't paint over a weakly typed language with strong types without causing all kinds of crazy edge cases. They're fundamentally different paradigms.

Are you asserting all developers are good with typing and its derivatives?
> as the worst of both world

YES YES YES. I'm witnessing this nightmare with a project that started out as untyped and now we're adding mypy to it because without typing it is difficult to understand and work on. 2 million lines of untyped python.

It's basically a huge technical debt and I doubt we'll ever get to the point where we can enforce this in CI

That doesn't mean gradual typing is the worst of both worlds though? It's bad but it's still not as bad as no static types.
it's worst of both worlds because types are not free. There is a certain amount of energy and effort and maintenance that you must put into type annotations. Before you reach the point of return on that investment you have to plow through the period of limbo where the cost of annotations is greater than the rewards. Some codebases never make it out of that morass.

One could easily make the argument that adding types to a preexisting codebase is more difficult than adding them to a greenfield project. So the effort and energy (and cost) is even higher.

Let's just say, you better be snuffing out an absolute fuckton of null reference and other bugs that a type system can actually help with for it to be worth it.

> Before you reach the point of return on that investment you have to plow through the period of limbo where the cost of annotations is greater than the rewards. Some codebases never make it out of that morass.

I disagree. In my experience of adding types to a terrible Python codebase the rewards are always greater than the cost, even if the effort is frustratingly high. In that situation the biggest rewards are that you make the code understandable, navigable and maintainable.

The frustrating effort must be paid whether or not you add static types. You can either pay it again and again over many years of people trying to figure out how the hell the code works and wasting time navigating and refactoring the slow way, or you pay it once up front and be done with it.

The only situation I can think of where it might not be worth it is if you have a codebase that is marked for death. If it's being replaced then.. eh. Leave it and let the problem solve itself. Otherwise I've found the effort to always be worth it.

The only big issue I've found is convincing colleagues of this. Especially colleagues that use Vim or Emacs and don't get half of the benefits of static types.

> Before you reach the point of return on that investment you have to plow through the period of limbo where the cost of annotations is greater than the rewards.

I'm a staunch supporter of static typing. But man, the amount of energy people put into getting things like mypy to go green in strict mode just doesn't make sense to me. There are going to be situations where the cost to avoid the mypy ignore on a line will *never* be worth the long term benefit. But nevertheless they persist

> why untyped languages became popular to begin with

because for a long time there were no mainstream languages with a good enough type system.

Types slow you down. New work is first completed in untyped languages. People start using the work. People complain there aren't types. Types are added. Work is now slow in the language. New work is now done in another language without types.

Rinse and repeat.

“Types slow you down” is the _stupidest_ excuse I hear all the time.

As if developers of untyped languages don’t spend ungodly amounts of time pretending types don’t exist, but needing to manually check them everywhere, wonder why shit blows up at runtime, litter their code with “typeof” style checks, litter their tests with type checking.

The types exist and need to be considered whether you believe it or not. Might as well let the computer help you out.

Or you know, pretend you’re toooooo cool for it.

There are maybe some programs where "time to first run" is a critical metric. But where I sit we spend way way way way more time living with a program after it has already been written. This means maintenance, bugfixes, and refactors.

Even if what you say is true, I'll trade some initial coding time to buy more efficient maintenance.

I dunno, I think the productivity promises of dynamic typing have been conclusively disproven, so maybe next time people will be able to at least say "we know that isn't a good idea".
Maybe there is an argument there for non-programmers in technical fields, that use programming at their jobs, but it isn't their primary job. Like a data scientist or architect, trying to use something convenient and easy. The problem is, that usually it catches up with them (and with bigger programs), in terms of poor quality code and bad habits. The argument then becomes if they should have learned better practices and habits in the first place. Sometimes what seems like a shortcut, is not really so.
that might have been true in the 2000s, but it doesn't apply to modern languages with things like generics, ADTs, type inference, null safety, etc.

you know what really slows me down? trying to use a function in a dynamic language, and having no god damned clue what type of arguments it accepts, and having to figure it out by grepping through the code or dynamically instrumenting a running program.

I think its more along the lines of things are prototyped without types, the proof of concept mostly works, then people realize that types are needed to reduce bugs, improve speed, and create a maintainable base as the project grows. What people should do is rewrite from scratch using the prototype as a vague guide, but instead people try to desperately fix the prototype incrementally. Its not the types that makes things slow, its the size of the codebase, and the lack of expectations on quality and performance.
If the language has clear and consistent ways to convert types when needed, the "slow down" from types is trivial.