Live data from Hacker News

Isolating complexity is the essence of successful abstractions

v5.chriskrycho.com

11–20 of 87 posts

Re: Isolating complexity is the essence of successful abstractions

#11
post #6

I don’t think I agree that either typescript nor rust successfully hide the complexity in their type systems. By the nature of type systems, they are tightly coupled with the code written around them. Rust has rich features to handle this coupling (traits and derives), but typescript does not.

Type systems like in Rust may introduce their own complexities, but they also help you tackle the complexity of bigger programs if wielded correctly.

Typesystems can be complex to use, but in the end they constrain the degrees of freedom exposed by any given piece of code. With a type systems only very specific things can happen with any part of your code, most of which the programmer may have had in mind — without a type system the number of ways any piece of code could act within the program is way larger. Reducing the possible states of your program in the case of programming error is a reduction of complexity.

Now I don't say type systems may introduce their own complexity, but in the case of Rust the complexity exposed is what systems programmers should handle. E.g. using different String types to signify to the programmer that your OS will not allow all possible strings as file names is the appropriate amount of complexity. Knowing how your program handles these is again reducing complexity.

Imagine you wrote a module in a language where you don't handle these. Every now and then the module crashes specifically because it came across a malformed filename. Or phrased differently: The program does more than you intended, namely crashing when it encounters certain filenames. Good luck figuring that out and preventing it from happening again. With a type system the choice had to be explicitly made during programming already. Less things you code can do, less complexity.

Many developers confuse complexity of the internal workings of a program with the complexity of the program exposed at the interface. These are separate properties that could become linked, but shouldn't.

Re: Isolating complexity is the essence of successful abstractions

#12
post #6

I don’t think I agree that either typescript nor rust successfully hide the complexity in their type systems. By the nature of type systems, they are tightly coupled with the code written around them. Rust has rich features to handle this coupling (traits and derives), but typescript does not.

I didn't get the general idea that the author thought they hid the complexity, but rather that they exposed and codified it. They gave the complexity that would previously live in your head somewhere it could be expressed. And once expressed, it can be iterated on.

Re: Isolating complexity is the essence of successful abstractions

#13
post #6

I don’t think I agree that either typescript nor rust successfully hide the complexity in their type systems. By the nature of type systems, they are tightly coupled with the code written around them. Rust has rich features to handle this coupling (traits and derives), but typescript does not.

The argument isn't that complexity is being hidden, but how it's managed and where it shows up in your experience of solving other problems. OP mentions:

> The complexity was always there... it merely shone a light on the existing complexity, and gave us the opportunity — and a tool with which — to start grappling with it

It's not about Rust vs. TypeScript per se but uses garbage collection and borrow checker as examples of two solutions to the same problem. For whatever task you have at hand, what abstractions offer the best value that lets you finish the solution to the satisfaction of constraints?

> they are tightly coupled with the code written around them

Which is where the cost of the abstractions comes in. Part of the struggle is when the software becomes more complicated to manage than the problems solved and abstractions move from benefit to liability. The abstractions of the stack prevent solving problems in a way that isn't bound to our dancing around them.

If I'm working on a high-throughput networked service shuffling bytes using Protobuf, I'm going to be fighting Node to get the most out of CPU and memory. If I'm writing CRUD code in Rust shuffling JSON into an RDBMS I'm going to spending more time writing and thinking about types than I would just shuffling around arbitrarily nested bag-of-bags in Python with compute to spare.

I always thought this was why microservices became popular, because it constrained the problem space of any one project so language abstractions remained net-positives.

Re: Isolating complexity is the essence of successful abstractions

#14
post #6

I don’t think I agree that either typescript nor rust successfully hide the complexity in their type systems. By the nature of type systems, they are tightly coupled with the code written around them. Rust has rich features to handle this coupling (traits and derives), but typescript does not.

It's not about hiding the complexity in the type system, that is, the complexity of the type system. At least for Rust, it's about that (yes, complex) type system isolating the even worse complexity of tracking lifetimes and aliasing and such, for all possible control flow paths, in your head.

It's harder to summarize what Typescript is isolating, except that JavaScript function signatures are the flipping wild west and the type system has to model most of that complexity. It tends to produce very leaky abstractions in my experience unless you put in a lot of work.

Re: Isolating complexity is the essence of successful abstractions

#15
post #6

I don’t think I agree that either typescript nor rust successfully hide the complexity in their type systems. By the nature of type systems, they are tightly coupled with the code written around them. Rust has rich features to handle this coupling (traits and derives), but typescript does not.

The argument isn't that complexity is being hidden, but how it's managed and where it shows up in your experience of solving other problems. OP mentions: > The complexity was always there... it merely shone a light on the existing complexity, and gave us the opportunity — and a tool with which — to start grappling with it It's not about Rust vs. TypeScript per se but uses garbage collection and borrow checker as exam…

> how it's managed and where it shows up in your experience of solving other problems

That’s what I’m talking about. Encoding complexity in your types does not manage where that complexity lives or where you have to deal with it.

It forces you to deal with that complexity everywhere in your codebase.

Re: Isolating complexity is the essence of successful abstractions

#16
post #12
post #6

I don’t think I agree that either typescript nor rust successfully hide the complexity in their type systems. By the nature of type systems, they are tightly coupled with the code written around them. Rust has rich features to handle this coupling (traits and derives), but typescript does not.

I didn't get the general idea that the author thought they hid the complexity, but rather that they exposed and codified it. They gave the complexity that would previously live in your head somewhere it could be expressed. And once expressed, it can be iterated on.

Encoding complexity in your type system forces you to deal with that complexity throughout your codebase. It doesn’t give complexity a specific place to live.

Re: Isolating complexity is the essence of successful abstractions

#17
post #16
post #12

Earlier quoted context omitted.

I didn't get the general idea that the author thought they hid the complexity, but rather that they exposed and codified it. They gave the complexity that would previously live in your head somewhere it could be expressed. And once expressed, it can be iterated on.

Encoding complexity in your type system forces you to deal with that complexity throughout your codebase. It doesn’t give complexity a specific place to live.

You were going to have to deal with that complexity either way.

Now it's expressed somewhere, and if you craft it right, enforced so it's harder to get things wrong.

https://lexi-lambda.github.io/blog/2019/11/05/parse-don-t-va...

Re: Isolating complexity is the essence of successful abstractions

#18
post #15

Earlier quoted context omitted.

The argument isn't that complexity is being hidden, but how it's managed and where it shows up in your experience of solving other problems. OP mentions: > The complexity was always there... it merely shone a light on the existing complexity, and gave us the opportunity — and a tool with which — to start grappling with it It's not about Rust vs. TypeScript per se but uses garbage collection and borrow checker as exam…

> how it's managed and where it shows up in your experience of solving other problems That’s what I’m talking about. Encoding complexity in your types does not manage where that complexity lives or where you have to deal with it. It forces you to deal with that complexity everywhere in your codebase.

> It forces you to deal with that complexity everywhere in your codebase.

The alternative is fighting the abstraction. Imagine trying to write the Linux Kernel in JavaScript or Python. Lot less fighting types in your code, more time fighting the abstractions to achieve other things. Considering a big part of the kernel is types it makes sense to encode complexity within them.

Going "low-level" implies that you're abandoning abstractions to use all the tools in the CS and compute toolbox and the baggage that entails.

Re: Isolating complexity is the essence of successful abstractions

#19

"Parameterizing complexity" is probably a better way to say it. There's no isolation when it comes to software.

Not sure if I agree

Let's say you have a poem program, that reads files from your drive and turns them into poems. A well isolated/abstracted variant of that program is as simple as a blackbox with two or three inputs and a single output.

One of the inputs are the files, the others might be a configuration file or user adjustable parameters like length. The program is well isolated if you can't give it any combination of inputs that doesn't produce a poem or an error message related to the usage of the program.

A badly isolated variant of the same program would be one where the user had to think a lot about the internal behavior of the program, e.g. how file names are handled or where so many parameters of the poem generation have to be supplied as parameters, that the user essentially has to rewrite the core of program with their parameters. Or the user could supply a file that allows them to gain RCE or crash the program.

Re: Isolating complexity is the essence of successful abstractions

#20
post #17
post #16

Earlier quoted context omitted.

Encoding complexity in your type system forces you to deal with that complexity throughout your codebase. It doesn’t give complexity a specific place to live.

You were going to have to deal with that complexity either way. Now it's expressed somewhere, and if you craft it right, enforced so it's harder to get things wrong. https://lexi-lambda.github.io/blog/2019/11/05/parse-don-t-va...

This view has always been bullshit. It doesn't differentiate between the complexity of the types themselves and the complexity of representing them in a static type system.
Post reply on HN