5 ms·
“Each move from one layer of the tech stack to a higher one involved a function: f(x) -> y Given a specific x, you always get a specific y as the artifa
by dpark 6mo ago
“Each move from one layer of the tech stack to a higher one involved a function:
f(x) -> y
Given a specific x, you always get a specific y as the artifact being generated.”
Not at all. If this were true then the Python code in question would generate deterministic binary. Of course that’s not what happens. The Python runs through an interpreter that may change behavior on different runs. It may change behavior version to version. It may even change behavior during multiple invocations of a function in the same running instance. Because all of that is abstracted away.
Same for the C code. You give up control and some determinism for the higher abstraction. You might get there same output between compilations on the same version but that’s not actually guaranteed and version to version consistent certainly isn’t.
Moving to a higher layer of abstraction very often results in less constrained behavior.
- slopinthebag 6mo agoCan you explain how Python or C programs change from invocation to invocation?
- dpark 6mo agoMostly because the behavior is implementation defined. So long as the behavior meets the contract, the compiler/interpreter is free to do whatever it wants. Python could certainly optimize repeated code paths to make them more efficient. I don’t know that the standard implementation actually does, but it could. Spending extra time optimizing repeated code paths is a reasonable choice for an interpreted or JIT compiled language. I would not expect C to change from invocation to invocation mostly because C is supposed to be boring and predictable. That’s kind of its thing. But again, it could. There’s nothing in the C spec I’m aware of that says the C compiler has to ensure that each invocation of a piece of code will execute the same machine instructions.
- slopinthebag 6mo agoOk but how does that change the behaviour of the program?
- dpark 6mo agoDepends on what you mean. Assuming you write code that does not take advantage of undefined behavior, then in general you would expect the correctness of your program to be consistent. But you would not expect the performance, for example, to be consistent. An optimizing JIT compiler might certainly run the 3rd invocation of code path way faster than the first.
- yuye 6mo ago>So long as the behavior meets the contract, the compiler/interpreter is free to do whatever it wants. Yes, and that's how it's supposed to be. Any description that determines the totality of a problem space is an implementation itself. Imagine the following requirements: f(0) = 0, f(2) = 4 Both f(x) = x^2 and f(x) = 2x are correct ways to implement said requirements. But if you start relying on f(1) = 2, you might get in trouble with a coworker that relies on f(1) = 1. This is undefined behavior and should be avoided. >There’s nothing in the C spec I’m aware of that says the C compiler has to ensure that each invocation of a piece of code will execute the same machine instructions. It can't, because C can be written for any system you want. If I ask the compiler to compile x *= 2, it might use the mul primitive or it might use shl, both are ok.
- HarHarVeryFunny 6mo agoThat's not a good analogy. With a high level language implementation of any sort, the actual instructions executed by the CPU may vary according to how it was compiled or run, or what machine you run it on, but the behavior will not. The high level language defines it's own level of abstraction, defining exact behavior, allowing the developer to have full control over program behavior, algorithms, UI, etc. An LLM + natural language instructions is not a program-like abstraction of what you want the computer to do, because it does not have that level of precision. Natural languages are fuzzy and imprecise, because they have been developed for communication, not precise machine-level specification. Obviously you can "vibe code" at different levels of detail, ranging from "build me an app to do X" to "here are 20 1000 word essays specifying the dos and don'ts of what I want you to build", but in either case you are nowhere near the level of precision of using a programming language to specify exactly what you want. So sure, "vibe coding" let's you accomplish A result with less attention to detail than using a programming language, but it's not a "higher level abstraction" in the sense that HLLs are, since it doesn't define what that abstraction is. It lets you get A result, but not define a SPECIFIC result, since natural languages just aren't that precise... natural language means whatever the person/thing interpreting it interprets it to mean. Of course you can use an LLM as a way to "rough out" a function or app, and as a crude tool to manipulate that roughed out form (or an existing project), but natural language does not have precise semantics and therefore cannot provide a precise definition of what you want to do.
- dpark 6mo agoIt wasn’t my analogy. It was the article’s so I responded to that. There are many more (and better) examples of how abstractions give up control, precision, and/or determinism. > The high level language defines it's own level of abstraction, defining exact behavior This is not entirely true. A high level language defines some behaviors, leaving many behaviors to be undefined and implementation specific. Many of those unspecified behaviors can matter in some cases. > It lets you get A result, but not define a SPECIFIC result, since natural languages just aren't that precise... You are repeating the same error as the article and missing the fact that while an abstraction lets you specify or control some things, it leaves many things out of your control. The higher the abstraction, the more stuff that is left out of your control. And maybe you don’t care about the things outside your control (great, the abstraction worked!) but regardless there are many things left unspecified in the typical abstraction and very often eventually you will care, which is why people say things like “all abstractions are leaky”. For a simple example, think of writing something like this: MessageBox(“hello world”, OkCancel) MessageBox is an abstraction over a massive amount of logic. You specified a string and a set of buttons and not much else. You give up control over the styling, the placement of the buttons, the actual button text (very likely will be localized), where the box will appear, and so much more. You are not getting a specific result. You are getting a result that meets the contract. “Write a program that shows a hello world message box” is a much higher level of abstraction than even that, and you are giving up significant specificity and determinism. Is it a good abstraction? That’s a great question. But it certainly is an abstraction.