Hacker Newsnew | past | comments | ask | show | jobs | submit | akiarie's commentslogin

Never mind, someone else already made this point: https://electric.ax/blog/2026/02/19/amdahls-law-for-ai-agent...

Thank you! This changed how I was thinking about my product: a knowledge harness for coding agents. While I was discussing incidents & context-switching tax, the structural compounding is likely the real win.

True, but thing is that "artificial intelligence" is meant in the opposite sense. It's meant as something that is / will be equivalent to human intelligence, not as something useful but vastly inferior.

i think its okay if "AI" is context dependant. but if you want a machine thats ~equal to human intelligence you can call it "AGI" and if its smarter, "ASI"

I propose that those of us who don't believe that AI is actually intelligence look for a term to refer to its "thinking" that isn't "intelligence", because this helps our weak minds keep things straight.


C is still, by far, the simplest language that we have.

Although many newer languages are safer (with the exclusion of Rust, primarily by being slower) the same kinds of issues that are there in C are there in these languages, their effects are just harder to see.

People complain about C as though they know how to fix it.


C is not a simple language in the sense that writing software in C is simple, and I think that's the only useful way to understand the word "simple" in this context.

Brainfuck is "simple" by any other definition as well, but that's not a useful quality.


C is a far simpler language than, for example, Swift. It's cognitive load in order to actually write something is pretty small - even the authors state that their book about C is intentionally slim because the concepts to understand are not that many.

That doesn't mean the C is a safer language than Swift, or a less-capable language than Swift. But in terms of "easy to understand along the happy-path", it's a lot easier to get going in C.

Swift, for example, bakes a whole load of CS-degree-level ideas and concepts into the basic language with its optionals, unwrapping, type-inference, async/await, existential types, ... ... ... . C doesn't do any of that. There are (many!) more footguns in C, but the language is less complex as a result.

Brainfuck is not at all simple, from that point of view. This is a valid Brainfuck program:

>+++++++++[<++++++++>-]<.>+++++++[<++++>-]<+.+++++++..+++.[-]>++++++++[<++++>-]<. >+++++++++++[<+++++>-]<.>++++++++[<+++>-]<.+++.------.--------.[-]>++++++++[<++++ >-]<+.[-]++++++++++.

This is the equivalent C program

#include <stdio.h> int main() { printf("Hello world!\n"); }

One of these is far simpler than the other.

[edit: changed to make the examples do the same thing]


The point I'm getting at is that your definition of "simple" (a word that should be banned among programmers) is not useful, if it is even meaningful.

The brainfuck example is "simpler": Only 8 kinds of tokens! Not really useful, though.

The cognitive load of _actually delivering software_ written in C is immensely greater than doing so with Swift, or Rust, or Python, or Java, even Zig, despite all of those leveraging much heavier machinery in order to deliver a friendlier abstract model for you to program against.

The tragedy of C is that, in addition only delivering very baseline abstraction tools, it also adds its own set of seemingly arbitrary rules and requirements that come from nowhere but the C standard. Fictitious limitations to suit a bygone era. The abstract model of C is fine in some places, but definitely not fine in other places, and my hypothesis is that most UB in practice comes from a mismatch between programmer intuitions and C's idiosyncracies.


Calling something "simple" to use and learn is a valid use of the word, sorry. Not going to stop doing that.

> The cognitive load of _actually delivering software_ written in C is immensely greater than doing so with Swift, or Rust, or Python, or Java, even Zig, despite all of those leveraging much heavier machinery in order to deliver a friendlier abstract model for you to program against

Sorry, I couldn't disagree more.

I find the simplicity of C to be elegant. You know the rules; it's like the entire C language is the 1-page summary of the encyclopaedia of C++ or Swift or Java, or (insert more-modern language here). The key to working well in C is in defining modular code with well-understood interfaces. I've got 40 years of programming in C so far, and the nightmare stories ran out after the first few years. Programming discipline is a thing.

Similarly, ObjC is a far superior, much simpler, object-oriented language than C++, there's about 15 different things over C, and you know the language. Template metaprogramming. Phooey! You'll still have to learn object-orientated programming semantics, but it's a "simple" language.

BTW: If you think the brainfuck language example is in any way easier to understand than the C one, I think you might need medication. /j


> I've got 40 years of programming in C so far, and the nightmare stories ran out after the first few years.

You need to find something more interesting to do ;)


Oh, I do. I'm building a two-story 1000 sq.ft garage right now - more workshop than garage tbh [1], I've just built a roll-off-roof observatory [2], the currently empty pad behind it is for a radio-telescope, last set up in London [3], still needs to be assembled in the new house. Right now I'm into the fun stuff of automating everything in the observatory. I've also recently taken up archery, and I'm enjoying that. I've written (well Claude has) an optimising compiler for a memory-managing language for the 6502 [4], but I'm just instrumenting (this bit is me) the IR so it can also target the M chip on my Mac. Eventually it'll also target m68k so I can bring up the Atari ST on the FPGA that is currently just emulating the atari 8-bit (I have a 120MHz 6502 at the moment :). The 'x' in 'xt' is from 'atari Xl' and the 't' is from 'atari sT'. The compiler is called 'xtc'. Both will run MiNT and the blitter on the FPGA is designed to integrate well with GEM, the graphics environment on the ST - even the XL version will have a graphical UI running at 1080p :)

So I have a few things to keep me busy right now.

1: http://0x0000ff.co.uk/img/garage/garage-layout.png

2: http://0x0000ff.co.uk/img/observatory/roll-off-roof-observat...

3: http://0x0000ff.co.uk/img/dish/dish.jpg

4: http://atari-xt.com/


Ooh, fun! Good luck!

another useful sense is easy to understand/read what programmer want to write. (ofc exclude mad code with macros etc).

Brainfuck is absolutely not simple in this case


C sits right in the middle between assembly and BASIC in terms of simplicity. You can't do a simple popcnt, but you can implement jump tables.

It's slower than Fortran and, depending on the platform, cobol. It's a bigger minefield than any language that came after it barring C++.

The only real advantage I can ascribe to C is that it's actually still being used after all these years, and it mostly works similarly on most hardware, like a Java for people who enjoy the casino.

Fixing C without breaking existing C code is pretty much impossible. You can start by defining warnings for UB, but then you will break any of the more trivial examples in the article. You can also start by simply killing off weird platforms (force a specific amount of bits for instance, screw the weird 16 bit char chips). Making casts explicit would probably fix a lot of problems too, though you'd need better syntax for that.

There is no fixing C without changing what C really is.


Can you elaborate what do you think C has in terms of simplicity that Zig doesn't, and which "same kinds of issues" do you think it has?

I'm not an expert in either language but my anecdotal experience disagrees with this - writing Zig has been far simpler and less error-prone than writing C.


"Verbose" is the wrong adjective. Yours is a terse projection into a lower space, valid in itself, but lacking the power and precision of its archetype.


This is such a beautiful distillation of everything I believe about the dangers of over-reliance on AI. I implore thee, good sir, to write a longer essay on this.


Obviously because he was one of the architects of the censorship regime of the late 2010s and early 2020s that nearly changed the internet into a three-letter-agency controlled space. If that isn't a risk for a censorship-resistant app, I don't know what is.


Is this true? My understanding was that Twitter was not really moderated, because of Dorsey?


Not even remotely.

  if (x > 0) {
    /* code */
  } else {
    /* more code */
  }
Branch coverage just means that all the lines above are executed in some test. That's fundamentally different from dealing with all the different values that `x` could take on at run time.


You're missing the point.

Given your example no software can ever handle this equation given infinite numbers, as all CPUs have a maximum number they can compare.

Just like you cannot prove that a bridge will be able to handle a given load. Engineering fundamentally can only make assurances within expectations. Your tests define these expectations, and by covering 100% branching coverage you've exhaustively explored all valid states your software can have.

Do note I'm talking about actual 100% branching coverage here, not just 100% coverage of your own code, ignoring libraries and runtimes you've imported. That's way harder then you seem to think and the reason why sqlite (the linked page) has so much more testing code then application code


0db is an interface for Xr0, so it's the same thing essentially.


Yeah but I think the difference is that Xr0 is the checker you run your code through and it spits out the result.


It cannot in general deduce the abstract. Doing so would require solving the halting problem.


It also can't in general deduce whether the abstract and the implementation match, as doing so would require solving the equivalence problem (which is undecidable for anything as or more powerful than pushdown automata).


I'm not very familiar with the equivalence problem, but at least for Xr0 abstracts (what we call the annotations attached to functions & loops) there is an extremely strong structural similarity between the implementation and the annotation. So it's not at all clear that we'll run into this.


> there is an extremely strong structural similarity between the implementation and the annotation.

If the annotation is basically just a copy of the function body why have any annotation at all?

> So it's not at all clear that we'll run into this.

It might not be clear to you, but if you want to allow any annotation with the same semantics as the body of the function you will run into this.


> If the annotation is basically just a copy of the function body why have any annotation at all?

This is a profound question. Xr0 annotations seem to be copies of the function bodies, but they aren't actually. What they are is a description of the function's safety semantics, the side effects and circumstances under which the function may be relied upon to exhibit well-defined behaviour (under the C Standard).

They only seem to be copies of the bodies because we're dealing with simple examples in the post here and in the tutorial, but we have a much more significant program ready (documenting) that we'll be sharing soon that shows the difference more clearly.

Xr0 is built in explicit reliance upon the powers of programmers to structure code in such a way that the annotations become simpler and simpler as you move up layers of function abstraction. As a simple example, think about what happens when you allocate memory and free it within a function, e.g.

        #include <stdlib.h>

        void
        foo() ~ [ return malloc(1); ]
        {
                return malloc(1);
        }

        void
        bar()
        {
                void *p = foo();
                free(p);
        }

        void
        baz()
        {
                bar(); /* completely safe */
        }
Note that `foo`'s annotation is like a copy of its body, but `bar`'s annotation is very simple, even though it interacts with `foo`, which is only safe if the pointer it returns is handled. So from `baz`'s perspective there are no safety semantics it has to worry about when it interacts with `bar`. The complexity that could lead to safety bugs in `foo` has been completely handled in `bar`.

We call this simplifying phenomenon denouement, and well-designed programs exhibit it very strongly. Our belief that programmers can design constructs that tend towards denouement as one moves down the call stack towards `main` is one of the basic reasons we're working on Xr0.

> It might not be clear to you, but if you want to allow any annotation with the same semantics as the body of the function you will run into this.

Yes, but we don't. Only well-defined behaviour according to the C Standard. This is the goal for Xr0 v1.0.0. After that the sky is the limit.


> Xr0 annotations seem to be copies of the function bodies, but they aren't actually.

You're constantly dodging the question. If annotations aren't copies of function bodies (which is really the only sensible choice), you need to deduce whether a function body matches the annotations. "Structural similarity" between C and your annotation syntax won't make this easier. In fact, it is impossible to deduce whether two C functions are semantically equivalent even though C is extremely structurally similar to C (you could even argue that C and C are structurally identical).

> Our belief that programmers can design constructs that tend towards denouement as one moves down the call stack towards `main` is one of the basic reasons we're working on Xr0.

So your main criticisms of Rust will mostly also apply to Xr0: You need to rewrite existing C code to make annotations practical (and at that point you might as well reimplement it in Rust) and Xr0 will limit the constructs you will use in your code, because annotations will be impractical for code that isn't written for "denouement".


> If annotations aren't copies of function bodies (which is really the only sensible choice), you need to deduce whether a function body matches the annotations. "Structural similarity" between C and your annotation syntax won't make this easier. In fact, it is impossible to deduce whether two C functions are semantically equivalent even though C is extremely structurally similar to C (you could even argue that C and C are structurally identical).

Maybe I don't understand your point. I am not denying that we're deducing that the body matches the annotations. I'm simply saying that for the restricted case of safety semantics alone, this deduction can be done. Yes, it is impossible to deduce semantic equivalence of arbitrary functions. The question is whether it is possible to deduce that annotations capture the safety semantics of the body. If you have a concrete example it would be helpful here – but for the restricted concerns of safety, not for arbitrary constructs.

> So your main criticisms of Rust will mostly also apply to Xr0: You need to rewrite existing C code to make annotations practical (and at that point you might as well reimplement it in Rust) and Xr0 will limit the constructs you will use in your code, because annotations will be impractical for code that isn't written for "denouement".

Your quote omits the first sentence of the paragraph, which states that "well-designed programs exhibit [denouement] very strongly". Denouement in the sense we're referring to is an absolute theoretical necessity for any safe program, because at some level (of functional abstraction) the safety concerns must be handled, otherwise the program would have a safety vulnerability.

Xr0 definitely limits constructs, but our claim is that the limitation we're imposing is one that reflects the structure of all safe programs. The same cannot be said about Rust's ownership semantics, which limit an enormous number of simple, safe constructs. So the C programs to which one would be adding Xr0 annotations wouldn't need to be rewritten unless a bug has been discovered.


> I'm simply saying that for the restricted case of safety semantics alone, this deduction can be done.

And I'm saying it can't be done, at least not in a fundamentally less tedious and hard way than Frama-C.

> Yes, it is impossible to deduce semantic equivalence of arbitrary functions. The question is whether it is possible to deduce that annotations capture the safety semantics of the body.

It isn't in general.

> If you have a concrete example it would be helpful here – but for the restricted concerns of safety, not for arbitrary constructs.

Safety is not a "restricted concern": You can for every property P easily construct a function that is safe if and only if property P holds. I'll be using Python because this example (which I like) requires arbitrary size integers. You could obviously also implement this in C, you'd just need to implement arbitrary size integers (or use a library that implements them):

    def collatz(x):
        if x % 2 == 0:
            return x // 2
        return x * 3 + 1

    def cycle(f, x):
        tortoise = f(x)
        hare = f(f(x))

        while tortoise != hare:
            tortoise = f(tortoise)
            hare = f(f(hare))

        return hare

    buffer = [0, 0, 0, 0, 0]
    x = int(input())
    if x >= 1:
        collatz_cycle_element = cycle(collatz, x)
        print(buffer[collatz_cycle_element]) # this is safe (or is it?)
This takes as an input an arbitrary positive integer x, searches for a cycle in the Collatz sequence beginning with that number and returns an arbitrary element of that cycle. It is conjectured that for every positive integer this cycle will be 4 -> 2 -> 1 -> 4 -> ... and thus this program is safe if and only if the Collatz conjecture holds.

Another example would be a C program that generates random planar graphs, computes their chromatic numbers and then collects statistics about them in an `int statistics[5]`:

    #include <stdio.h>

    void main() {
        int statistics[5] = {0};

        for(int i = 0; i < 1000; i++) {
            struct graph g = generate_random_planar_graph();
            int chromatic_number = compute_chromatic_number(g);
            statistics[chromatic_number] += 1;
        }
        
        printf("%i, %i, %i, %i", statistics[1], statistics[2], statistics[3], statistics[4]);
    }
The safety of this program requires you to prove that `generate_random_planar_graph` always returns a planar graph, that `compute_chromatic_number` correctly identifies the chromatic number and that the chromatic number of every planar graph is less than 5.


Thought provoking stuff! I really appreciate how much effort you've put into this.

However, the main reason why this argument is flawed is it omits the heart of the matter: the annotations. Xr0 empowers programmers to propagate safety semantics. A program that is only safe if the Collatz conjecture holds is (surprise) only safe if the Collatz conjecture holds. So in Xr0 the only requirement we would impose is that this program be augmented with an annotation that communicates that it is only safe if the Collatz conjecture is true.

So the flaw in the reasoning is we haven't claimed that Xr0 can prove arbitrary programs are safe. We've claimed that Xr0 can prove the correspondence between the safety semantics denoted in an annotation and a function body. Above there are no annotations given which would specify "this program is safe only if the Collatz conjecture is true". It shouldn't be hard to prove the correspondence between such an annotation and the program you've written, e.g.:

    def main(): ~ [
        buffer = [0, 0, 0, 0, 0]
        x = int(input())
        if x >= 1:
            collatz_cycle_element = cycle(collatz, x)
            print(buffer[collatz_cycle_element])
    ]

        buffer = [0, 0, 0, 0, 0]
        x = int(input())
        if x >= 1:
            collatz_cycle_element = cycle(collatz, x)
            print(buffer[collatz_cycle_element])

It's the principle of propagating the safety-determining factors of the function that we're stressing, not some kind of almighty power to judge that arbitrary constructs are safe or not.


> It's the principle of propagating the safety-determining factors of the function that we're stressing

That's the main function. It's the function that gets called when the program starts. Where do you want to propagate the "safety-determining factors" to?

If I'm just supposed to read the annotations on the `main` function (and those annotations can basically just be a copy of it's body), then why do I need the annotations at all? I could also read the program to determine whether it's safe.


It's because we're dealing with an extreme example in which a program's safety depends on the resolution of an open problem.

If the program's safety depends on the semantics of C and how they've been used in a function, it will be possible to deal with all the conditions upon which a program could be unsafe (in the sense of the standard safety vulnerabilities – like those listed here [0]). Doing so would lead to denouement, so that the annotation to the main function would be empty (meaning none of those bugs can occur).

Programs that might be unsafe should not be verifiable.

[0]: https://alexgaynor.net/2020/may/27/science-on-memory-unsafet...


> It's because we're dealing with an extreme example in which a program's safety depends on the resolution of an open problem.

It really isn't. The second example is what a reasonable C programmer would produce if you asked them to collect statistics about the chromatic number of random planar graphs. It also isn't based on any open problem. It's overall a very reasonable C program and a small one at that. It's safety also "depends on the semantics of C and how they've been used in a function", specifically on the fact that accessing an array is safe if and only if the index is in bounds. Out of bounds accesses to memory are probably the most common critical vulnerability in C programs, so it is essential that Xr0 prevents them.

I would love to see how you would write this reasonable program in a way that leads to "denouement".

> Programs that might be unsafe should not be verifiable.

Funnily enough, that's exactly what you criticize Rust for. It's not at all clear that writing programs with "denouement" is any less limiting than Rust. In fact, Frama-C (where you also try to write your code in such a way that the annotations become simpler) feels more limiting than Rust.


> It really isn't. The second example is ...

So will you admit that the first example has been sufficiently addressed? Because I was commenting on the problem involving the Collatz conjecture.

> It's safety also "depends on the semantics of C and how they've been used in a function", specifically on the fact that accessing an array is safe if and only if the index is in bounds. Out of bounds accesses to memory are probably the most common critical vulnerability in C programs, so it is essential that Xr0 prevents them.

True. As you said earlier:

> The safety of this program requires you to prove that `generate_random_planar_graph` always returns a planar graph, that `compute_chromatic_number` correctly identifies the chromatic number and that the chromatic number of every planar graph is less than 5.

This can obviously only be proven if we have the bodies of `generate_random_planar_graph` and `compute_chromatic_number`. Please provide these, and then I can attempt to answer. Because our whole point in Xr0 is that safety comes down to formalising interfaces – without interfaces for these functions we cannot investigate the safety of `main`.


> So will you admit that the first example has been sufficiently addressed? Because I was commenting on the problem involving the Collatz conjecture.

In that this specific program is obviously designed to be extremely hard to prove correct? Yeah. In that most real programs will be easier to prove correct automatically? No.

> This can obviously only be proven if we have the bodies of `generate_random_planar_graph` and `compute_chromatic_number`. Please provide these, and then I can attempt to answer. Because our whole point in Xr0 is that safety comes down to formalising interfaces – without interfaces for these functions we cannot investigate the safety of `main`.

What would be the point of that exactly? We both know that those functions would contain loops and as such Xr0 would be completely unable to verify them. We also know that Xr0 isn't actually able to state that `generate_random_planar_graph` generates a planar graph (other than completely repeating the body in the annotations, I guess). We also both know that Xr0 would not be able to deduce that the chromatic number of planar graphs is less than five.

I already provided two examples of programs the safety of which Xr0 will not be able to verify. I think it's your turn to produce a non-trivial C program that Xr0 will be able to handle.

This discussion really isn't all that enlightening, sadly. Xr0 can't currently handle loops and you haven't produced a single technical argument as to why I should expect Xr0 to be able to handle non-trivial loops in the future.

You also haven't produced a single technical argument as to why Xr0 should succeed where Frama-C failed. The argument that Frama-C is more general while Xr0 is specialized on safety is refuted easily as it is easy to come up with C programs the safety of which depends on arbitrary properties (and I would argue that many if not most real world C programs fall into this category).


One final point – I cannot emphasise strongly enough that we aren't making this for Rust programmers. There's nothing wrong with loving a programming language that has changed the world and what is possible in systems programming. Rust is an advance for systems programming, and it's made incredible strides.

But some programmers (like myself) love working in C, and do not like Rust's restrictions. For such programmers even if the question was one of re-implementation (which we don't think it is) Rust would remain undesirable.


Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: