Person A: "Programming language X is bad, code written in it is unreadable and horrible."
Person B: "No it's not. You can write good code in X, you just have to be disciplined."
Person A: "Programming language X is bad, code written in it is unreadable and horrible."
Person B: "No it's not. You can write good code in X, you just have to be disciplined."
Using design systems (languages) that eliminate entire classes of mistakes, because they make them impossible is the rational choice. It is human to make mistakes, but how we learn from the past to prevent mistakes in the future is where you can tell between engineers who truly care and those who are in it for their own ego.
Additionally, I really want us to stop pretending we can program CPUs directly (and I include the higher-level languages here as well, not just machine code and assembly). We can't. We need more declarative languages, e.g. I want to be able to say "I want to open a huge file with scatter-gather mechanics and with these 3 functions piped that do mapping and reduction". Why should we always code this by hand? There are objective best ways to produce this code in most languages. Let's have a DSL library that allows us to express that intent and be done with it and move on to be productive on another task.
This is not done enough to this day. Various people and organizations are doing it here and there and to me they are the beacons of our future hope. Everybody else is just banging at their Emacs / VIM in the meantime.
IME C++ templates and generic programming help with that aim.
Who are these people and organizations?
- I am writing the thing you are asking someone to write
- I have performance/memory constraints that existing high-level abstractions can’t adequately deal with
- I have legal or other business constraints preventing me from using existing high level tools (eg company prefers to build in house to minimize dependency risk exposure)
- I want to learn how that stuff works
- I am working on new technology that does not fit cleanly into the prescribed mold of the high level implementationsExample: in Elixir you have the so-called streaming functions you can pipe to each other that can do e.g. filtering, mapping, chunking and what not. But they don't actually operate on the data you passed them; they generate further functions and produce chains that only start working when you put a certain command at the end of the command pipe.
This works really well if you work with a collection of 100_000 elements. Since Elixir is an FP language, you don't want to copy this huge list, say, 7 times one after another (assuming your transformation pipe has 7 stages). By using the streaming functions you only use the original list and produce a final list. Just one roundtrip to the memory.
But... it has a performance impact and it's definitely not worth doing if your collections are e.g. less than 1000 elements, maybe even 2000.
A declarative DSL can easily cover for such cases and have separate code branches addressing them.
RE: tinkering, obviously. I am not saying normal programming should be "banned". But it's about time we have higher-order programming, man. We're all collectively slacking off on that and should be really ashamed!
The rest of your points I don't see how to automate and that's OK. Like any tool, declarative programming should be used sparingly and only where it truly applies.
However data races (a small but important subset of race conditions in concurrent software) are preventable, and so you can use languages which do so. Unlike the cat race condition I described above, data races are often unfathomable in real systems, the ultimate cause of truly mysterious and hard to reproduce bugs. So they're very much worth preventing.
Anyway, while it is true that "unbounded complexity" is potentially necessary, tools capable of such power should not be the default. When I spread jam on toast, I don't use the butcher's cleaver, I use a butter knife that's too blunt to cut flesh let alone bone. Which means, if I screw up the worst case is maybe I get jam on the carpet instead of the toast, not I cut my own hand off.
That difference in tooling can exist both within a language, and also between languages. There are many programs you cannot write in WUFFS, whereas there are (almost) no programs you can't write in C++. But that actually means we should use WUFFS when we can, because we don't want those programs. The image viewer where PNG files with long rectangular orange areas cause the hard disk to get reformatted, easily possible in C++ and not WUFFS. We don't want that program. The icon maker where a JPEG text comment with a shell script in it executes the script, easily possible in C++, not in WUFFS. Don't want that either.
I personally haven't seen them simply anything yet, but your assumption that verifying things with a dependent type would necessarily result in more complexity than a non-verified program is not trivial and needs support.
What really ticks me off about those kinds of people is that they are extremely likely to just have been blind about their own mistakes when coding C, while loudly claiming that "you just need to be disciplined".
This is the most baffling part for me. I'm yet to figure out who these mythical non-idiots are that would not have fallen for any of the pitfalls that have led to exploits in the last several decades.
Transformation according to logical invariants is basically the goal of compilation. Eschewing compilation creates a treadmill whereby a programmer thinks they're getting a lot done because they're mechanistically spitting out a large volume of code, but really they're acting as an imperfect compiler.
See also the large crowd of people writing JVM assembly (aka "Java"), as opposed to using higher level languages like Clojure or Scala. Yes JVM is leaps and bounds ahead of C, just like C is leaps and bounds ahead of machine assembler. But unless you're often writing things that existing compilers cannot, then personally standing in for a compiler is mostly just a way of introducing bugs.
Take nuclear reactors as an example. Some people seem to legitimately think that its possible to eliminate all sources of errors in such an immensely complex system. If there's been an accident, the analysis is generally "we have to add sections in the manual for this specific scenario and we'll be safe", rather than accepting that it's impossible to prevent all errors and that it's merely a question of time before the next anomaly arises.
When building complex things anomalies will happen which means we should follow good engineering practices and add extra safety systems.
In programming, and especially systems programming, people love to pretend they are infallible, to the detriment of everyone suffering from permanent firmware bugs and critical security infrastructure leaking their secrets.
That mostly describes amateurs. Designers of modern reactors generally think along the "let us minimize possible negative effects of a failure" lines.
So, no positive void coefficient (a horrible feature of RBMK in Chernobyl), addition of core catchers [0] etc.
1. Physical
2. Social
On the physical side they try to establish controls that prevent or mitigate issues or minimize the consequences of them. For instance, a modern reactor (probably, not my domain) is going to be designed in a way that certain meltdown situations can't occur (physically designed to separate reactor materials at certain thresholds, resulting in an automatic shutdown versus manual) or are properly contained (smaller reactors where a runaway reaction is more easily contained with modern materials at a reasonable cost).
On the social side there is training, regulatory controls, and an emphasis on safety culture/discipline. Discipline doesn't scale, but you can't totally avoid having it. This would be things like not turning off an alarm because it disrupts your nap (looking at you, Homer J. Simpson), but actually responding to it to determine its legitimacy (related concept: normalization of deviance). They'd also want to look at it from an HCI perspective and minimize false alarms and overwhelming operators with alarms or information that seem to be at the same level of importance, but aren't. So true critical issues are brought to the front as high priority, but less critical issues (ones that should be addressed but are lower priority) won't overwhelm the operator. (I've worked with others on this wrt avionics systems, alarm fatigue is real so you have to be choosy in which things get audible alarms and which get visual alarms.)
> If there's been an accident, the analysis is generally "we have to add sections in the manual for this specific scenario and we'll be safe",
In my experience, that's a belief held by the prideful and the novices.
> rather than accepting that it's impossible to prevent all errors and that it's merely a question of time before the next anomaly arises.
Anyone with a modicum of humility or experience will accept this and act accordingly.
Why don't these ancient projects just move to C++ for vectors and strings, that do the buffer handling correctly for you? Surely they can run a C++ compiler on any workstation these days?
C++ runtime is hardly huge by modern standards, and the usual resolution to ABI issues is to provide C bindings.
> Careless use of C++ -- Lack of judgement --
C doesn't make you immune to any of this. What are buffer overruns if not careless use of C?
Edit: Considering that I suggested moving to C++ for vectors and strings, your bringing up of TMP and polymorphism sounds like a strawman argument.
Maybe, but it seems unreasonable to hold the world back because of those systems.
> When it comes to bad judgement, C++ gives a lot more rope and especially rope that's C developers are not familiar with.
I think one could just ban most of that rope to junior devs. "Don't try to roll your own classes or templates, for getting them right requires both skill and discipline." Also common guidelines ban rope that isn't needed but for implementing the superior alternatives, that are usually found in the standard library.
You can still program in a procedural way and use just the low hanging fruits of the STL, that was designed for correctness above all, for Alex Stepanov made it to demonstrate his ideas of generic programming: C++ just happened to make implementing it possible.
Mind you, I am of the firm belief that C is at fault for many of the security vulnerabilities, but this is not one of them.
You shouldn't. They're partly right.
You are correct that "just be 100% disciplined 100% of the time" will fail. But "just get a language/compiler smart enough that I don't need discipline" will also fail.
Speaking from the point of view of a senior dev however, certain practices become ultra tedious and annoying from one point and on. Literal soul crushers.