No. Plausible code is syntactically-correct BS disguised as a solution, hiding a countless amount of weird semantic behaviours, invariants and edge cases. It doesn't reflect a natural and common-sense thought process that a human may follow. It's a jumble of badly-joined patterns with no integral sense of how they fit together in the larger conceptual picture.
HN user
bluetomcat
K&R C (2014)
We went from expressing computation via formal, mostly non-ambiguous languages with strict grammar and semantics, to a fuzzy and flaky probabilistic system that tries to mimic code that was already written. What could go wrong?
You are correct. The final output was polished by ChatGPT, but I originally presented the basic ideas in a few paragraphs. The LLM added a sense of flow and persuasiveness which are somehow lacking in my rather dry and non-native English writing style.
It starts from the identifier. At every stage, it outputs a sub-expression which is the “mirrored use” and corresponds to the boxed representation below it. When it reaches the top of the expression, it prints the final type of the expression which is the lone specifier-qualifier list.
As per the screenshot, “arr” is an array of 4 elements. Consequently, “arr[0]” is an array of 8 elements. Then, “arr[0][0]” is a pointer. And so on, until we arrive at the specifier-qualifier list.
And consequently, "you need 32GB of RAM just to be future-proof for the next 3 years".
Because in C, every allocation incurs a responsibility to track its lifetime and to know who will eventually free it. Copying and moving buffers is also prone to overflows, off-by-one errors, etc. The generic memory allocator is a smart but unpredictable complex beast that lives in your address space and can mess your CPU cache, can introduce undesired memory fragmentation, etc.
In Java, you don't care because the GC cleans after you and you don't usually care about millisecond-grade performance.
Yes, you can do it with minimal allocations - provided that the source buffer is read-only or is mutable but is unused later directly by the caller. If the buffer is mutable, any un-escaping can be done in-place because the un-escaped string will always be shorter. All the substrings you want are already in the source buffer. You just need a growable array of pointer/length pairs to know where tokens start.
Good C code will try to avoid allocations as much as possible in the first place. You absolutely don’t need to copy strings around when handling a request. You can read data from the socket in a fixed-size buffer, do all the processing in-place, and then process the next chunk in-place too. You get predictable performance and the thing will work like precise clockwork. Reading the entire thing just to copy the body of the request in another location makes no sense. Most of the “nice” javaesque XXXParser, XXXBuilder, XXXManager abstractions seen in “easier” languages make little sense in C. They obfuscate what really needs to happen in memory to solve a problem efficiently.
These lies don’t just affect them but also the people reading it as they might never see what actually happens
This is what sustains this whole economic bubble built on debt and future promises. At all levels of society, you have these inflated unrealistic expectations and BS circulating in the media. Technically-incompetent but eloquent and charismatic CEOs predict that in 6 months, some major technological shift will happen. Managers preach about adjusting their organisations to these new realities. Workers have no choice but to play the game with all its dirty tricks, if they want to stay employed. Anyone who dares to say that the emperor has no clothes is isolated in a dark corner because they may suddenly deflate the value of the whole economy. This is corporate feudalism disguised as a competitive economy.
They were popular because there was no Unix culture in Eastern Europe at the time. Pretty much any computer geek was a DOS user. To me personally, it always seemed kind of lame because many of these people would not bother to properly learn the shell language.
Their English is sufficiently good. It's a cultural aspect regarding writing style. When Russians and most Eastern Europeans write about technical subjects, they tend to be concise, dense and straightforward. Americans, on the other hand, are over-expressive and tend to saturate their writing with pointless metaphors and rhetorical devices.
Pattern matching should make the language less verbose, not more.
In the most basic cases, yes. It can be used as a more polished switch statement.
It's the whole paradigm of "define an ad-hoc Enum here and there", encoding rigid semantic assumptions about a function's behaviour with ADTs, and pattern matching for control-flow. This feels like a very academic approach and modifying such code to alter its opinionated assumptions isn't funny.
Rust encourages a rather different "high-level" programming style that doesn't suit the domains where C excels. Pattern matching, traits, annotations, generics and functional idioms make the language verbose and semantically-complex. When you follow their best practices, the code ends up more complex than it really needs to be.
C is a different kind of animal that encourages terseness and economy of expression. When you know what you are doing with C pointers, the compiler just doesn't get in the way.
The W210s did indeed rust badly and the interiors weren't on par with previous generations, but in purely mechanical terms, they were still solid cars. The diesels (particularly E250 TD and E290 TD) could cover 700k+ kilometres without any interventions to the engine or the transmission. The W211 is an improvement to the W210 in almost every aspect, and they are still plentiful on the roads in Eastern Europe.
What a mess of an article. A pretentious mishmash of scattered references with some vague abstract claims that could be summarised in one paragraph.
Big Tech drove us towards techno-feudalism. It's a wider social phenomenon and their hiring patterns for programmers are only one aspect of the problem. Small businesses are forced to do business on their platforms according to their rules, or else they go bust. Programmers are forced to learn their APIs, so that their "app" can live in their walled gardens. They soaked a huge amount of talent to optimise their ad and recommendation engines. This is a huge opportunity cost to society - that talent could be doing great creative stuff for small and medium-sized businesses instead.
With a lot of fancy wording, the article basically proposes that slow-moving, bureaucratic educational institutions should catch up with TikTok’s latest algorithm, helping raise the next generation of influencers.
Yes, that was my point. Regardless of the programming language, LLMs are glorified pattern matchers. A React/Node/MongoDB address book application exposes many such patterns and they are internalised by the LLM. Even complex code like a B-tree in C++ forms a pattern because it has been done many times. Ask it to generate some hybrid form of a B-tree with specific requirements, and it will quickly get lost.
It’s good at matching patterns. If you can frame your problem so that it fits an existing pattern, good for you. It can show you good idiomatic code in small snippets. The more unusual and involved your problem is, the less useful it is. It cannot reason about the abstract moving parts in a way the human brain can.
It’s a good direction to follow, but it can only get you so far. Some pieces of code do naturally evolve into a functional formalism, while others are inherently imperative. Usually, the top levels of your program (event loop) are imperative and deal with stateful “devices” like IO, the screen and storage subsystems. The “leaf” functions from the call graph can be functional, but you still can’t reason about the whole program when it is imperative at the top.
My thesis so far is something like "you should try to write little proofs in your head about your code." But there's actually a secret dual version of this post, which says "you should try to write your code in a form that's easy to write little proofs about."
Easier said than done. It is certainly feasible on greenfield projects where all the code is written by you (recently), and you have a complete mental model of the data layout and code dependencies. It's much harder to prove stuff this way when you call foo(), bar() and baz() across unit boundaries, when they modify global state and are written by different developers.
It’s not just skill atrophy. There’s the risk of homogenization of human knowledge in general. What was once knowledge rooted in an empirical subjective basis may become “conventional wisdom” reinforced by LLMs. Simple issues regarding one’s specific local environment will have generic solutions not rooted in any kind of sensory input.
Absolutely. LLMs offer you compressed documentation at your fingertips. Instead of scrolling through man pages and function references to find that specific flag or parameter you are interested in for the problem at hand, you can directly formulate such a question to the LLM, and it will give you a pretty good result.
I'm not sure if all this advice will become irrelevant or if those programmers trained in the 2020ies will not become those "best"..
It's how they use the AI. If they see it as a glorified StackOverflow where you paste a big chunk of code and ask "why does it not work", they'll be in trouble. If they are able to narrow-down their problems to a specific context, express them well and take the output of the AI with a grain of salt, they'll be 10x programmers compared to what we were in the 2000s, for example.
The idea of “traveling the world” when young is a very recent, very privileged and very Western perspective.
Growing up in post-communist Bulgaria during the 1990s and 2000s, I had never been abroad with my family as a child. At an age of 18 years, I started working from home as a PHP programmer for a guy from Switzerland I found online. When he invited me to his place in Basel for a 2-week visit, to me it felt like flying in space. I was valuing every second of my stay there like I am on another planet.
The perceived complexity from a semantic standpoint comes from the weakly-typed nature of the language. When the operands of an expression have different types, implicit promotions and conversions take place. This can be avoided by using the appropriate types in the first place. Modern compilers have warning flags that can spot such dodgy conversions.
The rest of the complexity stems from the language being a thin layer over a von Neumann abstract machine. You can mess up your memory freely, and the language doesn’t guarantee anything.
Wild guess: it was a way to offset the location of the "main" function by an arbitrary amount of bytes. In the a.out binary format, this translates to an entry point which is not zero.
Interesting usage of "extern" and "auto". Quite different from contemporary C:
tree() {
extern symbol, block, csym[], ctyp, isn,
peeksym, opdope[], build, error, cp[], cmst[],
space, ospace, cval, ossiz, exit, errflush, cmsiz;
auto op[], opst[20], pp[], prst[20], andflg, o, p, ps, os;
...
Looks like "extern" is used to bring global symbols into function scope. Everything looks to be "int" by default. Some array declarations are specifying a size, others are not. Are the "sizeless" arrays meant to be used as pointers only?Political action is fuelled by beliefs and ideals for the future. As a subjective observer, no one can attain a complete image of reality, or "the thing in itself". All we observe are appearances (phenomena) from our subjective point of view. In the process of a wider societal dialogue, we can exchange these points of view and eventually come to a rough approximation of the objective state of things.
They treat an OOM situation as exceptional and immediately call abort() in case any allocation function returns NULL. The specification of these functions allows you to handle OOM situations gracefully.