I don't think it's meant to be a comprehensive list, just a reminder that languages that many people consider "dead" or just "old fashioned" are in reality quite pervasive and power up many of our daily tools and apps.
... because those maintaining those dinosaurs haven't died yet and are holding back the next generation of programmers.
Thanks, green-fielders, for ignoring all Programming Language research. If it weren't for you we'd be "stuck" with Smalltalk instead of Java and Lisp instead of C. So thanks (not).
Are you sour because you don't understand pointers, or something? I'm 22 and I almost exclusively use C++, C, and x86 assembly. These types of languages are not going anywhere, and a fair portion "the next generation of programmers" is perfectly fine with that.
No. I understood and wrote tutorials on pointers when I was 17. I never understood what people don't get about them. They literally exist in real life in the back of any indexed book.
I'm just angry that new programmers still have to pay towards the technical debt of old school programmers.
It's sad to see that we're losing new programmers to old paradigms. Just so you know the state of the art, there are people out there mathematically proving that the operating system cores they wrote have no bugs. So long as you insist on managing everything yourself because "speed, man" you will continue to work overtime chasing bugs that aren't your fault (the fault is squarely on the paradigm called Von Neumann). Good luck wasting your life away making programs the most complicated way possible so you can feel productive. I'll be putting these Haskell Lego pieces together over here and let the compiler do all the work that you do manually. Because apparently your type of person (the type that accepts that status quo and thinks it's the best we've got and doesn't contribute to pushing for better things) will keep coming - these types of people are not going anywhere.
And yet, under almost every high-level language that "does all the work that I do manually", what is there?
Under Python in C++
Under PHP is C++
Under Haskell is a variant of C called C--
Under Lua is C
Under Perl is C
Is this because "the dinosaurs haven't died yet"? No. It's because writing a high-level programming language almost always requires a lower-level language. Under all of your high-level abstractions, there needs to be some compiles-to-assembly language that has access to memory management. Under all of your fancy high-level language debuggers, there needs to be some disassembly and translation of the assembly code to match the symbol databases for your high-level code. Under all of your binaries, there needs to be a kernel that can translate API calls, handle interrupts, dispatch exceptions, manage memory protection, and schedule threads.
Can you do all of this with Haskell? Sure. But the first Haskell compiler written in Haskell needs to be compiled by something. It can be C, C++, C--, or even assembly for all I care, but it has to exist. Even the first C compilers needed to be written in something that wasn't C.
The bottom line is that it has nothing to do with "technical debt". These languages exist because they can work on hardware without heavy frameworks weighing them down. They rely on the bare system, and not on a bunch of abstractions. And that's why they can be used as a layer between hardware and the much-needed abstractions brought to us by high-level languages.
These "old" languages have many strengths, and you're foolish to write them off just because their strengths don't fit your use-cases. Without the programmers that are "lost" to these "old paradigms", you would have nobody to maintain, develop, or secure your new paradigms. And that's a consequence that only considers one of the many strengths of these languages; there's many more consequences that we'd face if nobody new learned how to use them.
That's the Stroustrup argument - it's everywhere so it must be good! Like dust, or herpes.
Your argument is its own counter-argument. Why is everyone running away from C so badly that they are even willing to put up with PHP? That's how much C sucks. The evidence is in everyone making other languages as soon as they can to get away from C. Look at Lua. People cannot even put up with writing their whole applications in C, it's such a pain, they need an escape valve called Lua so they can breath a little.
That things are built atop C is the story of Academia in the USA. They have predominantly taught ALGOL-like languages and favored the Von Neumann architecture to complete detriment of Lambda Calculus. So it's because it's all people know, and they don't know any better. C is so much the wrong language to write programming languages in, that they wrote Yacc, Bison, etc because programming parsers and lexers are so impossibly difficult to write in such a limited language like C, that people gave up, and made tools to generate C code for them. Eww. And that's the industry standard, some smelly C code that vomits more C code that no one can write because C doesn't offer the appropriate abstractions to deal with the problem. That's technical debt. Your language is so weak no one has the balls to write a parser on it? Well guess what, throw it in the garbage and start over. Do not pass on that technical debt to further generations. If a language is weak, stop all work on it, move on to a stronger language that can express more things.
Which is why from very early on two schools of thought diverged and... just read "Worse is Better". :)
Point is, C people think worse is better, they don't want to take the time to make sure their stuff is solid because they like to brag that they get so much done and so clearly C must be better. I can make 10 sand castles in under an hour but when the sea flows it takes them with it. But no tsunami can take a stone castle. Why don't you want to strive to build stone castles?
As a final point, consider that your precious C programs and the C compiler can only run because of the CPU, which was not written in C nor in any Von Neumann language - oh no, they were written functionally, with only logic gates, in a dataflow-oriented way, because you know, they really needed this to work. That's why CPUs don't have bugs - they picked a better starting language than C, with a principled approach (logic) that is amenable to mathematical verification. Guess what - functional programming languages are also increasingly more amenable to mathematical verification. Once this is popularized, you'll be delegated to maintaining the old sand castles from dinosaurs while we will be building stone castles over here. Join us. :)
Detailed reply below, but I'm going to agree with the other child and say that you're vastly under-educated in this area and arguing from a point of ignorance. CPU's are not written in a "functional" manner, any similarity is superficial at best and certainly not anything to base practices on. CPU's are relatively, not completely as you so carelessly asserted, bug-free through a development process with high standards.
> As a final point, consider that your precious C programs and the C compiler can only run because of the CPU, which was not written in C nor in any Von Neumann language - oh no, they were written functionally, with only logic gates, in a dataflow-oriented way, because you know, they really needed this to work.
HDL's are not "only" logic gates. Have you developed anything substantial in a HDL? I'd think the lack of recursion alone would cleanly delineate them from anything functional. My own work with FPGA's started without language, literally wiring things together in a GUI. That's the legacy ASIC design descended from, not a functional ivory tower.
> That's why CPUs don't have bugs - they picked a better starting language than C, with a principled approach (logic) that is amenable to mathematical verification.
iHDL is not a "better" language than C. Imagine having to specify the exact opcode you wanted the linker to emit for a particular instruction, that's the level of dipping into implementation details that HDL's allow. It's not uncommon to have "behavioral" code that is checked in concert with the rest of the design, "implementation" code representing the actual circuit, and some poor engineer tasked with signing off that the two are equivalent without the ability to simulate all inputs. Formal verification is a specialized technique that is only used on vanishingly small parts of the design. At no point in the consideration of an HDL for a new design is "amenable to mathematical verification" given weight.
Another slight problem with your argument: CPU's DO have bugs. Google up "Intel Errata" and prepare for your stone castle to be torn apart. If you'd like to duck this and say that they're esoteric and don't matter, I'd really like to know what you think about the TSX-killing errata that's essentially set back parallel computing in a measurable way. Why wasn't this subjected to formal verification magic?
> Guess what - functional programming languages are also increasingly more amenable to mathematical verification. Once this is popularized, you'll be delegated to maintaining the old sand castles from dinosaurs while we will be building stone castles over here. Join us. :)
You don't understand what you're comparing to with enough fidelity to trust the end result.
The easiest way to imagine a CPU design team is that it's Just Another Software project. The only extra constraints are that you can't run it full speed. You can simulate a 5GHz chip at 5Hz with SW-like visibility. At some point in the alpha, the team will spend a few million dollars to wait 8 weeks to get a few dozen test chips that will run at full speed, with near-zero visibility. So the question is how much extra unit testing would you do, on each of those models, with those kind of timelines.
Formal verification doesn't make a dent on modern CPU design. It's all these extra considerations (read:bodies) taken during the design process that make up the gap. Please, try to educate yourself before going off on half-baked assumptions based on superficial similarities.
Thanks for the informative reply. A CPU might not be functional as in Haskell with recursion and types, but it is a weaker kind of functional in the sense that given the same inputs it will return the same outputs - it has no state, and even memory access is just a function to set or read from an address and those functions never fail, never throw null pointer exceptions, etc. Do you disagree that a circuit is more an example of data-flow paradigm than an example of the state-machine paradigm?
For a CPU to take the next step and decide what work to do at each clock cycle it needs only its inputs - it need not inspect the previous work it did in order to decide what to do next - it simply reads the next opcode the user wants to execute, and does it. That means it is referentially transparent. Which means it's tons more functional than C.
I will grant you though that I was wrong that CPUs never have bugs, they do and I appreciate your opening my eyes to that. Lots of interesting reading ahead of me. Let me ask you, then. Do you think CPUs have so few bugs just because of more stringent testing, or because of referential transparency and the fact that they a CPU is not a state machine like a C program is?
> it is a weaker kind of functional in the sense that given the same inputs it will return the same outputs - it has no state,
I'm trying to imagine what paucity of knowledge about a CPU you can have to say something like "no state." Registers? The program counter? The megabytes of caches? You've got to be stretching the definition of "state" beyond recognition. Each CPU has at least a few dozen, up to a few hundred, fuses covering everything from PLL tuning values to the vendor string.
CPU's are not deterministic, in the sense that given the same inputs it will return the same outputs. Have you never powercycled a system to have it come up when no "state" was changed? A single degree difference can cause transitions to different power states. A PLL might not lock on the same cycle. Electrical systems are ugly nightmares and it's just speaking to the quality of the abstractions that have been presented to you that you think otherwise.
> and even memory access is just a function to set or read from an address and those functions never fail, never throw null pointer exceptions, etc. Do you disagree that a circuit is more an example of data-flow paradigm than an example of the state-machine paradigm?
I'm not sure how to answer this. Everything's built as a series of state machines. Crack open a PRM and see state machines explicitly drawn out with all their transition arcs. If we're talking about generic "circuits," take a look at something like the PCIe LTSSM. PCIe is all about shoveling data from point to point, pretty dataflow-oriented, but it's behavior is specified as a combination of state machines.
> For a CPU to take the next step and decide what work to do at each clock cycle it needs only its inputs - it need not inspect the previous work it did in order to decide what to do next - it simply reads the next opcode the user wants to execute, and does it. That means it is referentially transparent. Which means it's tons more functional than C.
You're not talking about a "CPU" here. You're talking about the abstraction that a CPU provides about how it's assembly instructions will be interpreted. Normally it's not really an issue, people blur that all the time, but you're using statements about the abstraction to talk about the concrete. Map instead of the territory.
If CPU's were implemented in the fashion you describe they'd be incredibly slow. They HAVE to consider what they did before, what they're doing next, and how it all relates to the current instruction. There are prefetch blocks that just watch traffic, completely unaware of the actual instruction stream, just looking for patterns and optimistically pulling things into the cache. There's a ton of state just to keep up the illusion that things are happening sequentially, instruction by instructions, instead of out-of-order as possible to keep execution units humming.
> I will grant you though that I was wrong that CPUs never have bugs, they do and I appreciate your opening my eyes to that. Lots of interesting reading ahead of me. Let me ask you, then. Do you think CPUs have so few bugs just because of more stringent testing, or because of referential transparency and the fact that they a CPU is not a state machine like a C program is?
CPU's have so few bugs because of more stringent testing. There is no functional magic happening. It's just hard work by a lot of talented people. Please respect their effort.
> You're not talking about a "CPU" here. You're talking about the abstraction that a CPU provides about how it's assembly instructions will be interpreted.
This says it all. No, you are talking about assembly and pretending I don't know about registers. I'm talking about this:
Now that you know what I'm talking about, if you still insist on your point, and if you'd like to help me understand how I'm wrong, which I would appreciate, could you please show me how that live diagram would look like for a C program and its state, since as you say, C programs with its variables and the CPU with its registers are the same kind of "stateful".
A pointer that may or may not point to gibberish is not the same kind of stateful as RAM that is always a list of booleans no matter what happens. That's a pretty strong guarantee. That's a strong type. When in a C program do you enjoy the guarantee that something you are manipulating will always for sure be a certain way no matter what? CPU designers enjoy that guarantee towards registers, program counters, caches, and all the "state" you defend to be so alike that of C. There's a set number of registers and it never changes throughout execution. There's a set number of bits in RAM and it never changes throughout execution. Whether these bits over here and those over there are 0s or 1s are immaterial to the CPU because guess what, the CPU does not care, it does not understand the semantics of my program, and so it can never take a "wrong" step - any "error" is my fault for coding the wrong instructions. (Except for the occasional CPU bug as I have been shown exist).
Please stop trying to talk about assembly. I know assembly. I've programmed Win32 assembly (you can make fun of me, it's fine). I'm not talking about assembly here. I'm talking about the CPU designers or programmers and the guarantees they have when programming that we rarely do in HLLs.
Think of this another way: say you are designing/coding a CPU. Isn't every possible error that can happen your fault? Is that the case in C? Whose fault is it if the computer runs out of memory? If memory gets corrupted? If a hacker pokes your memory to cheat in a game? It's no one's fault, those things can't be predicted. But when designing a CPU you can be sure you have the power to predict every possible thing that can happen in your problem domain. Because you're designing a dataflow grid and an adjunct list of booleans whose semantics are immaterial to you. But in C it's just not possible (nor in any other language, C is just a stand-in here) because you are invested in the semantics.
You don't know what you're talking about. I worked on CPU design teams for 8 years of my professional career. I'm telling you how they're actually built and don't need to use phrases like "CPU takes its steps" as if that's meaningful. Your conceptions about how CPUs are designed, how the design team actually reasons about what they're building, is wrong. It is not correct. You have no evidence this is actually how those people think.
> if you still insist on your point, and if you'd like to help me understand how I'm wrong, which I would appreciate, could you please show me how that live diagram would look like for a C program and its state, since as you say, C programs with its variables and the CPU with its registers are the same kind of "stateful".
I wouldn't venture near such a misguided statement. The closest I'd come would be something along the lines of "At the sequence points of a C program, parallels could be drawn to specific structures in the CPU responsible for tracking that state." Note that those are meaningful words that someone could describe as "wrong" with an explanation, not groping in the dark for "stateful" to be stretched to cover both cases or whatever you were accusing me of doing/not doing. But even then, the entire state of a C program at a sequence point might not co-exist temporally inside a CPU that nevertheless returns a correct result so it would take some more guard language.
> There's a set number of registers and it never changes throughout execution.
This is a prime example where it's really clear you haven't considered a CPU architecture from the last decade or so. There are a set number of architectural registers. Inside the actual chip, because we're considering a window of 40 uops instead of one macroinstruction at a time there are an indefinite number of dynamically assigned registers that may potentially hold the value of one architectural register. Look up "Register Renaming" for yet another tables stakes CPU arch concept you're blissfully unaware of.
This is probably going to keep happening. You really don't know enough about the subject matter to make correct or wrong statements. As is, your statement isn't precise enough to know if you were wrong about physical registers or right about abstracted architectural registers. Note that being "right" about architectural registers means your argument is based on an abstraction instead of implementation. And you really wanted to be talking about implementation.
> Please stop trying to talk about assembly. I know assembly. I've programmed Win32 assembly (you can make fun of me, it's fine). I'm not talking about assembly here. I'm talking about the CPU designers or programmers and the guarantees they have when programming that we rarely do in HLLs.
I mentioned assembly once, in passing as a durable layer of abstraction. I also mentioned a whole host of physical reasons why your input/output determinism is false, all of which are equally as devastating to your argument as the one comment you've zeroed in on for no particular reason. I'd appreciate at least acknowledging that something as finicky as a PLL probably has to be included in a discussion about modern CPU's. Were you going to address any of that or pretend it didn't happen?
> Think of this another way: say you are designing/coding a CPU. Isn't every possible error that can happen your fault? Is that the case in C? Whose fault is it if the computer runs out of memory? If memory gets corrupted? If a hacker pokes your memory to cheat in a game? It's no one's fault, those things can't be predicted. But when designing a CPU you can be sure you have the power to predict every possible thing that can happen in your problem domain. Because you're de...
I don't know how to explain myself any better to you. I'm not disputing what you are saying nor your authority. I'm talking about stuff much beneath what you are talking about which is why I can barely address what you are saying.
I'll give this one last shot and provide some links. That's the best I can do.
As Wikipedia puts it, "An entire processor can be created using NAND gates alone". That's a functional program, whether I'm able to convince you of it or not. Whether there are tradeoffs taken in the pragmatic world of manufacturing sell-able CPUs is beyond what I'm talking about. You may know a lot about this which is why I don't dispute any of what you say, but you are not talking about the same CPU I'm talking about. You are talking about real world CPUs which is fine. It's just not what I'm talking about. If real-world CPU designers prefer to use state in their processor design that's up to them - but the nature and essence of a CPU obviates state, as a bunch of NAND gates suffices for one as Wikipedia puts it.
I'll leave some links here if you are interested in knowing the kind of things that led me to be so misinformed. Thanks for engaging in this discussion. I appreciate your assuming I'm ignorant instead of evil. Every logical gates tutorial I have ever read (including the first result in Google for me: http://cpuville.com/logic_gates.htm) says something like "These gates can be combined to make the larger circuits needed to make a computer processor." which as you can understand may lead a lay person like me (just a programmer, not a CPU designer like you) to believe that processors are therefore functional. Clearly I'm missing something, but I'm not sure I know what.
> As Wikipedia puts it, "An entire processor can be created using NAND gates alone". That's a functional program, whether I'm able to convince you of it or not.
We're going to explain why you're wrong down below, but a quick note on the logic you're trying to use upon that faulty premise: Just because some processor could potentially be made in a functional manner does not imply that commercial CPUs are done that way. Do you remember why we're talking?
> As a final point, consider that your precious C programs and the C compiler can only run because of the CPU, which was not written in C nor in any Von Neumann language - oh no, they were written functionally, with only logic gates, in a dataflow-oriented way
You weren't talking hypothetical. You were talking about CPU's that people actually use and their relative stability compared to the terrible software that runs on them. It's really obvious to me that this statement, the entire reason I'm here at all talking to you, was not talking about an abstract Platonic CPU. That you're not admitting it was incorrect and retreating to this ridiculous absurdity is boggling. But you've managed to be wrong yet again, so let's not dwell on the past and just dive into that.
> If real-world CPU designers prefer to use state in their processor design that's up to them - but the nature and essence of a CPU obviates state, as a bunch of NAND gates suffices for one as Wikipedia puts it.
NAND gates can be combined in such a way as to retain state. The way this reads, I think you're assuming that since a single NAND gate cannot retain state it must follow that any combination of them cannot retain state?
Look up "D Flip Flop". Made out of NAND alone, retains state. Given how often you talk about functional things, I'm really really surprised you didn't think about the possibility of feeding an output back into an input. The thing you're missing is what combinations are lurking under that description. I don't think you did the slightest bit of imagining or research on those combinations before declaring all circuitry functional.
> Clearly I'm missing something, but I'm not sure I know what.
Luckily we've found it! You thought all circuits were limited to Combinatorial logic. You were unaware of Sequential logic. I hope you will reconsider the <i>nature</i> and <i>essence</i> of CPU's in light of this new information.
Unfortunately that's not it - I'm aware of sequential logic and its dependency on past signals - just like the `foldp` function in functional-reactive programming allows one to depend on past signals. If you think that's stateful, then foldp is a state-manipulating function and Haskell is finally impure: https://hackage.haskell.org/package/helm-0.4/docs/FRP-Helm-S...
I didn't make a statement about haskell and I'm not interested in parsing definitions that you've shown gleeful disregard for as recently as two posts ago. The 2003 paper isn't using "functional" in your pet manner and I'm not interested in educating such a hostile student. You're clearly reaching for material you don't understand in hopes that I'll be overwhelmed and it's not a good look. If you'd like to pull one of the three (3) usages of "functional" from the 2003 paper and explain how you thought it relevant to your argument I'd be enlightened. If you're just going to come back with a new raft of papers you don't understand from 30 years ago, please save us both the time and spare me. I'm not interested in reading the results of your uneducated googling and teaching you what you found.
CPU's are "amenable" to verification in the same way C programs are. Throw enough human bodies, enough time, enough blood, enough sweat, and enough tears, at the creation of a software object and it will be "verified" to be correct. There's nothing inherent in C programs that precludes them from being put through this stringent process. There's a lot of costing reasons why people don't spend that time and effort on a field-patchable software object that they do on gates costing millions of dollars to permanently etch into silicon, but I never said anything approaching "CPUs are so amenable to verification while C programs aren't" because I don't believe your argument to be true. In fact, I'd argue that something like the MRC proves that C-like stateful procedural languages can be put through the same process and result in something just as durable as a CPU, if we're somehow admitting you've been talking about commercial CPU's instead of the goofy abstraction you claimed upthread.
Are you planning on addressing how architecturally defined registers aren't stateful? That's a concrete example that posters other than myself also zeroed in on. The only argument given so far was pinned on the NAND equivalence being stateless. It's disappointing you didn't deign to address that in between your wild accusations and gleeful misrepresentations about what I've been saying.
> Thanks for the informative reply. A CPU might not be functional as in Haskell with recursion and types, but it is a weaker kind of functional in the sense that given the same inputs it will return the same outputs - it has no state, and even memory access is just a function to set or read from an address and those functions never fail, never throw null pointer exceptions, etc. Do you disagree that a circuit is more an example of data-flow paradigm than an example of the state-machine paradigm?
This is all kinds of wrong. CPUs have tons of state. Registers, program counters, TLB, CPU mode, cache, etc. You're just abstracting all that state away as "input," Using this criteria, you can say that a C program with a global buffer is stateless because the global buffer is just an input for the functions.
You're incorrect on your mental model of functional pureness anyway. Functional languages have state, it is just that the state is immutable.
> For a CPU to take the next step and decide what work to do at each clock cycle it needs only its inputs - it need not inspect the previous work it did in order to decide what to do next - it simply reads the next opcode the user wants to execute, and does it. That means it is referentially transparent. Which means it's tons more functional than C.
Incorrect. A modern CPU will often look at different stages in its pipeline in order for other stages to progress. This is one of the reasons you have instruction re-ordering. You can also hit a point where you have to flush the pipeline and start your operation over because you had a branch prediction miss.
In other words, CPUs are highly stateful. You are simply incorrect.
Since they are basically the same and I was just abstracting away what was convenient to me. Hey, if I'm wrong, I'm wrong.
I think there's a difference. The reason we can make a live diagram like that for CPUs is that they don't care about the "state" they operate, and hence are immune from problems therefrom. C programs care about the state they operate (they take conditional jumps on expressions that might be null pointers) but CPUs don't. Every branch test the CPU runs is for sure run over a register and for sure will either succeed or fail, there's no third choice, no bottom, no chance of it going wrong.
I recently read someone saying that there exist so many interpreted languages built on top of C because no one really wants to write C, they would rather have a language built on top of another language.
After starting to learn pure C myself, I have to agree.
Specifically the part that says Avoid gratuitous negativity. That means that your first sentence could probably be phrased in a different way, for example in terms of why you think the list of technologies isn't meaningful, or why it's a bad metric. On HN it's also generally frowned upon to complain about things being posted - if you feel it's truly off-topic, you are free to use the flagging privilege. This requires a certain amount of karma to use, though.
I once watched a video about languages by I believe the designer of F# (could be wrong). He talked about a graph with a horizontal axis measuring "elegance". On the right hand end is your highly elegant mostly functional languages. On the left hand side are the rugged languages like C++ very few people would call it elegant. However on the vertical axis is Utility. on the bottom are "not very useful at all", and at the top is "very useful". These languages are solidly in the upper left hand. The ultimate goal is to find something on the upper right hand side... but nothing has quite achieved it yet.
> These [low elegance] languages are solidly in the upper left hand [high utility].
This may be the result of survivor bias. Of course there are also non-elegant languages that aren't useful, but these won't appear in such a diagram, because nobody remembers or uses them anymore.
(Exceptions may be esoteric/fun languages like Brainfuck, although these could be considered "elegant" from a certain point of view.)
I really like sqlite, lua and luajit's "C" implementations.
For C++ I think Qt is pretty reasonable, also the Unreal Engine.
I'm using Java at work at the moment (Google Web Toolkit mainly, and learning it on the fly), but feeling the love in there. Some things are crazy (enums :)), others looked strange at the begining (dependency injection), but I really like the interface / class split - the interface "Set" vs the implementation "HashSet" (or something else).
38 comments
[ 5.0 ms ] story [ 83.6 ms ] threadThanks, green-fielders, for ignoring all Programming Language research. If it weren't for you we'd be "stuck" with Smalltalk instead of Java and Lisp instead of C. So thanks (not).
I'm just angry that new programmers still have to pay towards the technical debt of old school programmers.
It's sad to see that we're losing new programmers to old paradigms. Just so you know the state of the art, there are people out there mathematically proving that the operating system cores they wrote have no bugs. So long as you insist on managing everything yourself because "speed, man" you will continue to work overtime chasing bugs that aren't your fault (the fault is squarely on the paradigm called Von Neumann). Good luck wasting your life away making programs the most complicated way possible so you can feel productive. I'll be putting these Haskell Lego pieces together over here and let the compiler do all the work that you do manually. Because apparently your type of person (the type that accepts that status quo and thinks it's the best we've got and doesn't contribute to pushing for better things) will keep coming - these types of people are not going anywhere.
Under Python in C++
Under PHP is C++
Under Haskell is a variant of C called C--
Under Lua is C
Under Perl is C
Is this because "the dinosaurs haven't died yet"? No. It's because writing a high-level programming language almost always requires a lower-level language. Under all of your high-level abstractions, there needs to be some compiles-to-assembly language that has access to memory management. Under all of your fancy high-level language debuggers, there needs to be some disassembly and translation of the assembly code to match the symbol databases for your high-level code. Under all of your binaries, there needs to be a kernel that can translate API calls, handle interrupts, dispatch exceptions, manage memory protection, and schedule threads.
Can you do all of this with Haskell? Sure. But the first Haskell compiler written in Haskell needs to be compiled by something. It can be C, C++, C--, or even assembly for all I care, but it has to exist. Even the first C compilers needed to be written in something that wasn't C.
The bottom line is that it has nothing to do with "technical debt". These languages exist because they can work on hardware without heavy frameworks weighing them down. They rely on the bare system, and not on a bunch of abstractions. And that's why they can be used as a layer between hardware and the much-needed abstractions brought to us by high-level languages.
These "old" languages have many strengths, and you're foolish to write them off just because their strengths don't fit your use-cases. Without the programmers that are "lost" to these "old paradigms", you would have nobody to maintain, develop, or secure your new paradigms. And that's a consequence that only considers one of the many strengths of these languages; there's many more consequences that we'd face if nobody new learned how to use them.
Your argument is its own counter-argument. Why is everyone running away from C so badly that they are even willing to put up with PHP? That's how much C sucks. The evidence is in everyone making other languages as soon as they can to get away from C. Look at Lua. People cannot even put up with writing their whole applications in C, it's such a pain, they need an escape valve called Lua so they can breath a little.
That things are built atop C is the story of Academia in the USA. They have predominantly taught ALGOL-like languages and favored the Von Neumann architecture to complete detriment of Lambda Calculus. So it's because it's all people know, and they don't know any better. C is so much the wrong language to write programming languages in, that they wrote Yacc, Bison, etc because programming parsers and lexers are so impossibly difficult to write in such a limited language like C, that people gave up, and made tools to generate C code for them. Eww. And that's the industry standard, some smelly C code that vomits more C code that no one can write because C doesn't offer the appropriate abstractions to deal with the problem. That's technical debt. Your language is so weak no one has the balls to write a parser on it? Well guess what, throw it in the garbage and start over. Do not pass on that technical debt to further generations. If a language is weak, stop all work on it, move on to a stronger language that can express more things.
Which is why from very early on two schools of thought diverged and... just read "Worse is Better". :)
Point is, C people think worse is better, they don't want to take the time to make sure their stuff is solid because they like to brag that they get so much done and so clearly C must be better. I can make 10 sand castles in under an hour but when the sea flows it takes them with it. But no tsunami can take a stone castle. Why don't you want to strive to build stone castles?
As a final point, consider that your precious C programs and the C compiler can only run because of the CPU, which was not written in C nor in any Von Neumann language - oh no, they were written functionally, with only logic gates, in a dataflow-oriented way, because you know, they really needed this to work. That's why CPUs don't have bugs - they picked a better starting language than C, with a principled approach (logic) that is amenable to mathematical verification. Guess what - functional programming languages are also increasingly more amenable to mathematical verification. Once this is popularized, you'll be delegated to maintaining the old sand castles from dinosaurs while we will be building stone castles over here. Join us. :)
> As a final point, consider that your precious C programs and the C compiler can only run because of the CPU, which was not written in C nor in any Von Neumann language - oh no, they were written functionally, with only logic gates, in a dataflow-oriented way, because you know, they really needed this to work. HDL's are not "only" logic gates. Have you developed anything substantial in a HDL? I'd think the lack of recursion alone would cleanly delineate them from anything functional. My own work with FPGA's started without language, literally wiring things together in a GUI. That's the legacy ASIC design descended from, not a functional ivory tower.
> That's why CPUs don't have bugs - they picked a better starting language than C, with a principled approach (logic) that is amenable to mathematical verification. iHDL is not a "better" language than C. Imagine having to specify the exact opcode you wanted the linker to emit for a particular instruction, that's the level of dipping into implementation details that HDL's allow. It's not uncommon to have "behavioral" code that is checked in concert with the rest of the design, "implementation" code representing the actual circuit, and some poor engineer tasked with signing off that the two are equivalent without the ability to simulate all inputs. Formal verification is a specialized technique that is only used on vanishingly small parts of the design. At no point in the consideration of an HDL for a new design is "amenable to mathematical verification" given weight.
Another slight problem with your argument: CPU's DO have bugs. Google up "Intel Errata" and prepare for your stone castle to be torn apart. If you'd like to duck this and say that they're esoteric and don't matter, I'd really like to know what you think about the TSX-killing errata that's essentially set back parallel computing in a measurable way. Why wasn't this subjected to formal verification magic?
> Guess what - functional programming languages are also increasingly more amenable to mathematical verification. Once this is popularized, you'll be delegated to maintaining the old sand castles from dinosaurs while we will be building stone castles over here. Join us. :) You don't understand what you're comparing to with enough fidelity to trust the end result.
The easiest way to imagine a CPU design team is that it's Just Another Software project. The only extra constraints are that you can't run it full speed. You can simulate a 5GHz chip at 5Hz with SW-like visibility. At some point in the alpha, the team will spend a few million dollars to wait 8 weeks to get a few dozen test chips that will run at full speed, with near-zero visibility. So the question is how much extra unit testing would you do, on each of those models, with those kind of timelines.
Formal verification doesn't make a dent on modern CPU design. It's all these extra considerations (read:bodies) taken during the design process that make up the gap. Please, try to educate yourself before going off on half-baked assumptions based on superficial similarities.
For a CPU to take the next step and decide what work to do at each clock cycle it needs only its inputs - it need not inspect the previous work it did in order to decide what to do next - it simply reads the next opcode the user wants to execute, and does it. That means it is referentially transparent. Which means it's tons more functional than C.
I will grant you though that I was wrong that CPUs never have bugs, they do and I appreciate your opening my eyes to that. Lots of interesting reading ahead of me. Let me ask you, then. Do you think CPUs have so few bugs just because of more stringent testing, or because of referential transparency and the fact that they a CPU is not a state machine like a C program is?
I'm trying to imagine what paucity of knowledge about a CPU you can have to say something like "no state." Registers? The program counter? The megabytes of caches? You've got to be stretching the definition of "state" beyond recognition. Each CPU has at least a few dozen, up to a few hundred, fuses covering everything from PLL tuning values to the vendor string.
CPU's are not deterministic, in the sense that given the same inputs it will return the same outputs. Have you never powercycled a system to have it come up when no "state" was changed? A single degree difference can cause transitions to different power states. A PLL might not lock on the same cycle. Electrical systems are ugly nightmares and it's just speaking to the quality of the abstractions that have been presented to you that you think otherwise.
> and even memory access is just a function to set or read from an address and those functions never fail, never throw null pointer exceptions, etc. Do you disagree that a circuit is more an example of data-flow paradigm than an example of the state-machine paradigm?
I'm not sure how to answer this. Everything's built as a series of state machines. Crack open a PRM and see state machines explicitly drawn out with all their transition arcs. If we're talking about generic "circuits," take a look at something like the PCIe LTSSM. PCIe is all about shoveling data from point to point, pretty dataflow-oriented, but it's behavior is specified as a combination of state machines.
> For a CPU to take the next step and decide what work to do at each clock cycle it needs only its inputs - it need not inspect the previous work it did in order to decide what to do next - it simply reads the next opcode the user wants to execute, and does it. That means it is referentially transparent. Which means it's tons more functional than C.
You're not talking about a "CPU" here. You're talking about the abstraction that a CPU provides about how it's assembly instructions will be interpreted. Normally it's not really an issue, people blur that all the time, but you're using statements about the abstraction to talk about the concrete. Map instead of the territory.
If CPU's were implemented in the fashion you describe they'd be incredibly slow. They HAVE to consider what they did before, what they're doing next, and how it all relates to the current instruction. There are prefetch blocks that just watch traffic, completely unaware of the actual instruction stream, just looking for patterns and optimistically pulling things into the cache. There's a ton of state just to keep up the illusion that things are happening sequentially, instruction by instructions, instead of out-of-order as possible to keep execution units humming.
> I will grant you though that I was wrong that CPUs never have bugs, they do and I appreciate your opening my eyes to that. Lots of interesting reading ahead of me. Let me ask you, then. Do you think CPUs have so few bugs just because of more stringent testing, or because of referential transparency and the fact that they a CPU is not a state machine like a C program is?
CPU's have so few bugs because of more stringent testing. There is no functional magic happening. It's just hard work by a lot of talented people. Please respect their effort.
This says it all. No, you are talking about assembly and pretending I don't know about registers. I'm talking about this:
http://www.visual6502.org/JSSim/index.html
In other words, how the CPU takes its steps.
Now that you know what I'm talking about, if you still insist on your point, and if you'd like to help me understand how I'm wrong, which I would appreciate, could you please show me how that live diagram would look like for a C program and its state, since as you say, C programs with its variables and the CPU with its registers are the same kind of "stateful".
A pointer that may or may not point to gibberish is not the same kind of stateful as RAM that is always a list of booleans no matter what happens. That's a pretty strong guarantee. That's a strong type. When in a C program do you enjoy the guarantee that something you are manipulating will always for sure be a certain way no matter what? CPU designers enjoy that guarantee towards registers, program counters, caches, and all the "state" you defend to be so alike that of C. There's a set number of registers and it never changes throughout execution. There's a set number of bits in RAM and it never changes throughout execution. Whether these bits over here and those over there are 0s or 1s are immaterial to the CPU because guess what, the CPU does not care, it does not understand the semantics of my program, and so it can never take a "wrong" step - any "error" is my fault for coding the wrong instructions. (Except for the occasional CPU bug as I have been shown exist).
Please stop trying to talk about assembly. I know assembly. I've programmed Win32 assembly (you can make fun of me, it's fine). I'm not talking about assembly here. I'm talking about the CPU designers or programmers and the guarantees they have when programming that we rarely do in HLLs.
Think of this another way: say you are designing/coding a CPU. Isn't every possible error that can happen your fault? Is that the case in C? Whose fault is it if the computer runs out of memory? If memory gets corrupted? If a hacker pokes your memory to cheat in a game? It's no one's fault, those things can't be predicted. But when designing a CPU you can be sure you have the power to predict every possible thing that can happen in your problem domain. Because you're designing a dataflow grid and an adjunct list of booleans whose semantics are immaterial to you. But in C it's just not possible (nor in any other language, C is just a stand-in here) because you are invested in the semantics.
You don't know what you're talking about. I worked on CPU design teams for 8 years of my professional career. I'm telling you how they're actually built and don't need to use phrases like "CPU takes its steps" as if that's meaningful. Your conceptions about how CPUs are designed, how the design team actually reasons about what they're building, is wrong. It is not correct. You have no evidence this is actually how those people think.
> if you still insist on your point, and if you'd like to help me understand how I'm wrong, which I would appreciate, could you please show me how that live diagram would look like for a C program and its state, since as you say, C programs with its variables and the CPU with its registers are the same kind of "stateful".
I wouldn't venture near such a misguided statement. The closest I'd come would be something along the lines of "At the sequence points of a C program, parallels could be drawn to specific structures in the CPU responsible for tracking that state." Note that those are meaningful words that someone could describe as "wrong" with an explanation, not groping in the dark for "stateful" to be stretched to cover both cases or whatever you were accusing me of doing/not doing. But even then, the entire state of a C program at a sequence point might not co-exist temporally inside a CPU that nevertheless returns a correct result so it would take some more guard language.
> There's a set number of registers and it never changes throughout execution.
This is a prime example where it's really clear you haven't considered a CPU architecture from the last decade or so. There are a set number of architectural registers. Inside the actual chip, because we're considering a window of 40 uops instead of one macroinstruction at a time there are an indefinite number of dynamically assigned registers that may potentially hold the value of one architectural register. Look up "Register Renaming" for yet another tables stakes CPU arch concept you're blissfully unaware of.
This is probably going to keep happening. You really don't know enough about the subject matter to make correct or wrong statements. As is, your statement isn't precise enough to know if you were wrong about physical registers or right about abstracted architectural registers. Note that being "right" about architectural registers means your argument is based on an abstraction instead of implementation. And you really wanted to be talking about implementation.
> Please stop trying to talk about assembly. I know assembly. I've programmed Win32 assembly (you can make fun of me, it's fine). I'm not talking about assembly here. I'm talking about the CPU designers or programmers and the guarantees they have when programming that we rarely do in HLLs.
I mentioned assembly once, in passing as a durable layer of abstraction. I also mentioned a whole host of physical reasons why your input/output determinism is false, all of which are equally as devastating to your argument as the one comment you've zeroed in on for no particular reason. I'd appreciate at least acknowledging that something as finicky as a PLL probably has to be included in a discussion about modern CPU's. Were you going to address any of that or pretend it didn't happen?
> Think of this another way: say you are designing/coding a CPU. Isn't every possible error that can happen your fault? Is that the case in C? Whose fault is it if the computer runs out of memory? If memory gets corrupted? If a hacker pokes your memory to cheat in a game? It's no one's fault, those things can't be predicted. But when designing a CPU you can be sure you have the power to predict every possible thing that can happen in your problem domain. Because you're de...
I'll give this one last shot and provide some links. That's the best I can do.
As Wikipedia puts it, "An entire processor can be created using NAND gates alone". That's a functional program, whether I'm able to convince you of it or not. Whether there are tradeoffs taken in the pragmatic world of manufacturing sell-able CPUs is beyond what I'm talking about. You may know a lot about this which is why I don't dispute any of what you say, but you are not talking about the same CPU I'm talking about. You are talking about real world CPUs which is fine. It's just not what I'm talking about. If real-world CPU designers prefer to use state in their processor design that's up to them - but the nature and essence of a CPU obviates state, as a bunch of NAND gates suffices for one as Wikipedia puts it.
I'll leave some links here if you are interested in knowing the kind of things that led me to be so misinformed. Thanks for engaging in this discussion. I appreciate your assuming I'm ignorant instead of evil. Every logical gates tutorial I have ever read (including the first result in Google for me: http://cpuville.com/logic_gates.htm) says something like "These gates can be combined to make the larger circuits needed to make a computer processor." which as you can understand may lead a lay person like me (just a programmer, not a CPU designer like you) to believe that processors are therefore functional. Clearly I'm missing something, but I'm not sure I know what.
A functionally verified implementation of a CPU: (keywords: VAMP, DLX) http://www.kroening.com/papers/charme2003.pdf
Formal verification of an ARM processor: http://www.cs.cmu.edu/~bryant/pubdir/vlsi99.vishnu.pdf
We're going to explain why you're wrong down below, but a quick note on the logic you're trying to use upon that faulty premise: Just because some processor could potentially be made in a functional manner does not imply that commercial CPUs are done that way. Do you remember why we're talking?
> As a final point, consider that your precious C programs and the C compiler can only run because of the CPU, which was not written in C nor in any Von Neumann language - oh no, they were written functionally, with only logic gates, in a dataflow-oriented way
You weren't talking hypothetical. You were talking about CPU's that people actually use and their relative stability compared to the terrible software that runs on them. It's really obvious to me that this statement, the entire reason I'm here at all talking to you, was not talking about an abstract Platonic CPU. That you're not admitting it was incorrect and retreating to this ridiculous absurdity is boggling. But you've managed to be wrong yet again, so let's not dwell on the past and just dive into that.
> If real-world CPU designers prefer to use state in their processor design that's up to them - but the nature and essence of a CPU obviates state, as a bunch of NAND gates suffices for one as Wikipedia puts it.
NAND gates can be combined in such a way as to retain state. The way this reads, I think you're assuming that since a single NAND gate cannot retain state it must follow that any combination of them cannot retain state?
Look up "D Flip Flop". Made out of NAND alone, retains state. Given how often you talk about functional things, I'm really really surprised you didn't think about the possibility of feeding an output back into an input. The thing you're missing is what combinations are lurking under that description. I don't think you did the slightest bit of imagining or research on those combinations before declaring all circuitry functional.
> Clearly I'm missing something, but I'm not sure I know what.
Luckily we've found it! You thought all circuits were limited to Combinatorial logic. You were unaware of Sequential logic. I hope you will reconsider the <i>nature</i> and <i>essence</i> of CPU's in light of this new information.
You still haven't explained to me how come CPUs are so amenable to verification while C programs aren't. Another example, explicitly mentioning Sequential Logic: http://www.cs.cmu.edu/~emc/papers/Papers%20In%20Refereed%20J...
Finite State Machines are amenable to verification. I think (not sure) that must be the difference I'm trying to convey.
CPU's are "amenable" to verification in the same way C programs are. Throw enough human bodies, enough time, enough blood, enough sweat, and enough tears, at the creation of a software object and it will be "verified" to be correct. There's nothing inherent in C programs that precludes them from being put through this stringent process. There's a lot of costing reasons why people don't spend that time and effort on a field-patchable software object that they do on gates costing millions of dollars to permanently etch into silicon, but I never said anything approaching "CPUs are so amenable to verification while C programs aren't" because I don't believe your argument to be true. In fact, I'd argue that something like the MRC proves that C-like stateful procedural languages can be put through the same process and result in something just as durable as a CPU, if we're somehow admitting you've been talking about commercial CPU's instead of the goofy abstraction you claimed upthread.
Are you planning on addressing how architecturally defined registers aren't stateful? That's a concrete example that posters other than myself also zeroed in on. The only argument given so far was pinned on the NAND equivalence being stateless. It's disappointing you didn't deign to address that in between your wild accusations and gleeful misrepresentations about what I've been saying.
This is all kinds of wrong. CPUs have tons of state. Registers, program counters, TLB, CPU mode, cache, etc. You're just abstracting all that state away as "input," Using this criteria, you can say that a C program with a global buffer is stateless because the global buffer is just an input for the functions.
You're incorrect on your mental model of functional pureness anyway. Functional languages have state, it is just that the state is immutable.
> For a CPU to take the next step and decide what work to do at each clock cycle it needs only its inputs - it need not inspect the previous work it did in order to decide what to do next - it simply reads the next opcode the user wants to execute, and does it. That means it is referentially transparent. Which means it's tons more functional than C.
Incorrect. A modern CPU will often look at different stages in its pipeline in order for other stages to progress. This is one of the reasons you have instruction re-ordering. You can also hit a point where you have to flush the pipeline and start your operation over because you had a branch prediction miss.
In other words, CPUs are highly stateful. You are simply incorrect.
Could you please show me a C program live-diagrammed like the following CPU: http://www.visual6502.org/JSSim/index.html
Since they are basically the same and I was just abstracting away what was convenient to me. Hey, if I'm wrong, I'm wrong.
I think there's a difference. The reason we can make a live diagram like that for CPUs is that they don't care about the "state" they operate, and hence are immune from problems therefrom. C programs care about the state they operate (they take conditional jumps on expressions that might be null pointers) but CPUs don't. Every branch test the CPU runs is for sure run over a register and for sure will either succeed or fail, there's no third choice, no bottom, no chance of it going wrong.
Well, Redshift is in C with some Python
Meanwhile Javascript is "so ubiquitous that making a list seems a bit pointless."
:)
Abstraction is a beautiful thing, but we should not forget our roots!
After starting to learn pure C myself, I have to agree.
BTW, Stroustrup himself has a list of applications written in C++: http://www.stroustrup.com/applications.html
In case you haven't seen it already, I would like to draw your attention to the HN guidelines posted here: https://news.ycombinator.com/newsguidelines.html
Specifically the part that says Avoid gratuitous negativity. That means that your first sentence could probably be phrased in a different way, for example in terms of why you think the list of technologies isn't meaningful, or why it's a bad metric. On HN it's also generally frowned upon to complain about things being posted - if you feel it's truly off-topic, you are free to use the flagging privilege. This requires a certain amount of karma to use, though.
This may be the result of survivor bias. Of course there are also non-elegant languages that aren't useful, but these won't appear in such a diagram, because nobody remembers or uses them anymore.
(Exceptions may be esoteric/fun languages like Brainfuck, although these could be considered "elegant" from a certain point of view.)
For C++ I think Qt is pretty reasonable, also the Unreal Engine.
I'm using Java at work at the moment (Google Web Toolkit mainly, and learning it on the fly), but feeling the love in there. Some things are crazy (enums :)), others looked strange at the begining (dependency injection), but I really like the interface / class split - the interface "Set" vs the implementation "HashSet" (or something else).
/me raises hand
The following things are green;
trees grass moss apples ...