Live data from Hacker News

How could the early Unix OS comprise so few lines of code?

retrocomputing.stackexchange.com

131–140 of 170 posts

Re: How could the early Unix OS comprise so few lines of code?

#131
post #126

Earlier quoted context omitted.

It had warts, and I'm vaguely tempted to make another, cleaner, attempt at it with the assorted lessons learned - I don't work there any more and the parent company has ditched all the code, and so while I can't release that code, writing a new version from scratch wouldn't compete with anything they do. Not top of my list at the moment, though.

Any sufficiently powerful API could be misused anyway. Don’t have any regrets for not adding another one into the mix.

For it to make sense, it'd need to be the full package, with UI components. The nice thing was being able to annotate the model and get a "good enough for an MVP" CRUD ui out of it that'd significantly beat a generic database browser; not just the API.

Unfortunately that's also accordingly more work. I need a project that'll actually need it first...

Re: How could the early Unix OS comprise so few lines of code?

#132
post #111

Earlier quoted context omitted.

> instead inside the slice-typed variable which lives somewhere else entirely. OK... where? Now you have a complicated heap-like semantic inside your compiler internals. But not everyone wants their string metadata in the heap. So now you need allocator semantics a-la C++, which ultimately leads to move semantics, etc... Good luck getting that done in 1972. No, DMR was right, Pascal was wrong, and fancy modern string…

> OK... where? In the automatic storage, duh, where everybody else puts them today. > But not everyone wants their string metadata in the heap. Exactly, so you don't put them there, which is what I am proposing: instead of putting the string length before, or the null delimiter after, the string itself, you put it near the pointer that points to the beginning of the (sub)string.

> you put it near the pointer that points to the beginning of the (sub)string.

And now you have a "string pointer" which is distinct from a "data pointer". You can't allocate a heap block and "put a string in it" because the special thing at the start needs to go with the pointer and not the data in the block. And your 1970's compiler on your PDP-11 with 48kb of RAM needs to manage that. Good luck to you.

Again, we're not answering the question "Can a better string implementation than C be designed?". Clearly the answer is yes. Go pick your favorite, there are literally dozens. We're asking "What else should Richie have done with C that would have been better?". And... it's not what you're suggesting for sure.

Re: How could the early Unix OS comprise so few lines of code?

#133
post #115

Earlier quoted context omitted.

Dealing with edge cases is where your code blows up. Once you find an edge case do you leave it unaddressed and the program generate a wrong/unexpected answer? Could it be a potential security flaw? Unfortunately when we write initial specifications we almost certainly get them wrong in one way or another. Trying to deal with that after the fact commonly leads to application bloat.

I think the most important thing to ask about an edge case is “how could I have designed this code to make it not an edge case”

"I should have beat the customer until they gave me full specifications of what they needed before writing code"

Is unfortunately what happens a lot.

Re: How could the early Unix OS comprise so few lines of code?

#134
post #130
post #115

Earlier quoted context omitted.

Dealing with edge cases is where your code blows up. Once you find an edge case do you leave it unaddressed and the program generate a wrong/unexpected answer? Could it be a potential security flaw? Unfortunately when we write initial specifications we almost certainly get them wrong in one way or another. Trying to deal with that after the fact commonly leads to application bloat.

My experience is that while edge cases certainly adds bloat, there are plenty of cases where the bloat comes from entirely different things, and where simply rewriting with the hindsight of being able to see the overall structure better can do wonders. In other words: A lot of the time undoing mistakes made first time around as a result of not having the full picture. And often it's a result of trying to cater for ev…

In a converse reply, Microsoft has done very well financially by holding on to backwards compatibility.

In the OSS world we tend to look at rewrites as "This is for me, who gives a shit about the customer", but most customer (paying) facing software there are the expectations of Do not break the application, and do not break the customers expectations.

This said, X was and is a mess.

Re: How could the early Unix OS comprise so few lines of code?

#135
post #132

Earlier quoted context omitted.

> OK... where? In the automatic storage, duh, where everybody else puts them today. > But not everyone wants their string metadata in the heap. Exactly, so you don't put them there, which is what I am proposing: instead of putting the string length before, or the null delimiter after, the string itself, you put it near the pointer that points to the beginning of the (sub)string.

> you put it near the pointer that points to the beginning of the (sub)string. And now you have a "string pointer" which is distinct from a "data pointer". You can't allocate a heap block and "put a string in it" because the special thing at the start needs to go with the pointer and not the data in the block. And your 1970's compiler on your PDP-11 with 48kb of RAM needs to manage that. Good luck to you. Again, we'r…

> And now you have a "string pointer" which is distinct from a "data pointer". You can't allocate a heap block and "put a string in it" because the special thing at the start needs to go with the pointer and not the data in the block.

The second sentence doesn't follow from the first one.

    struct string {
        char   (*ptr)[static len];
        size_t len;
    };

    struct string new_copy = { .ptr = malloc(100), .len = 100; };
    new_copy = strcpy(new_copy, "some other string"); // string literal is a syntactic sugar for statically allocated 'struct string' with 'ptr' set to an unnamed, statically allocated char[] buffer inside it, and proper 'len' field.

    struct string strcpy(struct string dest, struct string src) {
        struct string result = { .ptr = dest.ptr, .len = min(dest.len, src.len) };
        memcpy(result.ptr, src.ptr, result.len);
        return result;
    }
Or you can do

    struct string *new_copy = malloc(100 + sizeof(struct string));
    new_copy->ptr = (char*)(new_copy + 1);
    new_copy->len = 100;
if you really want to. Just don't pass naked "char*" around, that thing has no obvious extent.

Re: How could the early Unix OS comprise so few lines of code?

#136
post #134
post #130

Earlier quoted context omitted.

My experience is that while edge cases certainly adds bloat, there are plenty of cases where the bloat comes from entirely different things, and where simply rewriting with the hindsight of being able to see the overall structure better can do wonders. In other words: A lot of the time undoing mistakes made first time around as a result of not having the full picture. And often it's a result of trying to cater for ev…

In a converse reply, Microsoft has done very well financially by holding on to backwards compatibility. In the OSS world we tend to look at rewrites as "This is for me, who gives a shit about the customer", but most customer (paying) facing software there are the expectations of Do not break the application, and do not break the customers expectations. This said, X was and is a mess.

My point isn't so much that ditching the backwards compatibility is always right, even when it lets you shed lots of code, but that you can often massively reduce code size that way. Whether that's the right thing to do is often a fine balance, because it depends a great deal on how many people actually care about the old ways, and sometimes people will get it very wrong in either direction.

Sometimes the 10x more code is actually worth it. But you should be aware when it is 10x more code, and make sure it is worth it.

Re: How could the early Unix OS comprise so few lines of code?

#137

I feel that most 100K line programs could be rewritten with just 10K lines and end up being more reliable. Feature creep is responsible for some of the code bloat but I can guarantee from experience that, in the vast majority of projects, you could keep all the features and still cut the code to at least 1/10th of its size. I think the reason for this is because developers who focus on development speed do so at the…

> I think the reason for this is because developers who focus on development speed do so at the expense of succinctness. The more foresight you have when you're writing code, the fewer lines you will end up with.

Not necessarily.

1. The process you describe works pretty well for code you wrote yourself. However, once the original developers are gone, any understanding, foresight and future plans they had (beyond the coarsest, most high level ones) basically got burned in a fire. That means a lot of minimum-effort jiu-jitsu solutions aren't available anymore, and you'll have a lot more kludges and re-implementations of things that may already be there (but forgotten).

2. Few developers have the luxury of infinite time, and succinct code is rarely a business priority. That means you pretty much never can go all-in for succinctness, which would frequently require large scale refactors to achieve.

The end result is that, inevitably, over time, features and bug fixes will get grafted onto foundations that weren't ever meant to support them.

Re: How could the early Unix OS comprise so few lines of code?

#138
post #136
post #134

Earlier quoted context omitted.

In a converse reply, Microsoft has done very well financially by holding on to backwards compatibility. In the OSS world we tend to look at rewrites as "This is for me, who gives a shit about the customer", but most customer (paying) facing software there are the expectations of Do not break the application, and do not break the customers expectations. This said, X was and is a mess.

My point isn't so much that ditching the backwards compatibility is always right, even when it lets you shed lots of code, but that you can often massively reduce code size that way. Whether that's the right thing to do is often a fine balance, because it depends a great deal on how many people actually care about the old ways, and sometimes people will get it very wrong in either direction. Sometimes the 10x more co…

Rewriting the code isn't the hard part...

The QA is the hard part.

Developer: "Rewriting this line of code shouldn't change anything"

Narrator: It did

Re: How could the early Unix OS comprise so few lines of code?

#139
post #102
post #97

Earlier quoted context omitted.

Toy solutions deal with small data inputs. How many lines of that 20k is just optimizing for large inputs (you can’t have long repeating sections in small inputs).

I've written a linear-time regex matcher in 65 lines of code: https://jasonhpriestley.com/regex

What features does it support? What if I pass in a complex regexp with data several GB in size?

There is a large difference between toy examples and hardened enterprise ready code.

Re: How could the early Unix OS comprise so few lines of code?

#140
post #102

Earlier quoted context omitted.

I've written a linear-time regex matcher in 65 lines of code: https://jasonhpriestley.com/regex

What features does it support? What if I pass in a complex regexp with data several GB in size? There is a large difference between toy examples and hardened enterprise ready code.

The features are less important than the linear complexity.

It lowers to an NFA. An NFA can recognise any linear language, so adding more features affects the generation of the NFA, while increasing the performance involves faster matching against the NFA.

In this case he's done a followup with benchmarks where he's converting the NFA to a DFA ajd comparing favourably against both Node and your browers regexp engine with only a tiny little bit extra code:

https://jasonhpriestley.com/regex-dfa

That doesn't mean there isn't value to the rest of those 20k lines of code I referenced - that was not the point.

A lot of them adds a bunch of convenience, like a more expressive syntax that saves you from writing more convoluted regexps, and it presumably bought more performance than their previous, smaller iteration, every step up from a much smaller engine way back. I'm sure one could do better with less, but I'm also not dismissing that I'm sure each step was reasonable given the constraints.

But that too is also not the point. It was not about dismissing the size of the Ruby regexp engine as not worth it.

The point was that there is a very significant and rapid diminishing return, and that this explains why you can do so seemingly much with so very little, because the leap from no capability to something usable takes very little, but each subsequent increment will tend to buy you less, for more work.

That doesn't mean people should stop putting in that extra work and squeeze out a bit more. It just gives an answer to the question in the link.

Post reply on HN