I identify with @antirez's sentiment. I know both C and C++ very well. I find C definitely more artistic. C++ is utilitarian.
Prolog is the other language I code in "artistically". It too is quite simple and constrained, though it is on the opposite end of the high/low-level spectrum as C.
/unpopular-hot-take
I've never used Ruby but from what I've seen I understand it to be similar to Perl in that regard.
Of course good naming and abstractions are of key importance, and comments and inline documentation are important finishing touches, which so many programmers don't seem to have the time for. It doesn't hurt that Larry Wall, the designer of perl, was a linguist. Perl was meant to be flexible and expressive.
And everyone knows that there's nothing worse to read than obfuscated perl. Perhaps some think that is artistic in a different way, to make the shortest and most unreadable program possible. Not my thing, personally.
I would argue that C++ can be dramatically more deceiving than C --- see inheritance and operator overloading, just to name two.
Note how terrible doing any amount of math is in C.
I don’t understand that. You can have functions that return promises that then can get passed to other functions returning promises, and leave it either to the compiler or to an expression evaluator you write (that ideally runs at compilation time as much as possible) to optimize away anything not needed. For example (pseudo-code)
vector3D a = …
vector3D b = …
promise<vector3D> c = addLazily(a,b)
print c.x
in the end, could do the equivalent of print a.x + b.x
I think you could make a modern C++ compiler do that for this simple example.Also, there’s a simple bijection between expressions with operators and two-argument function calls:
a + b * c
+(a, *(b,c))
plus(a, times(b,c))
Because of that, I don’t understand why operator overloading should give better optimization opportunities than function calls.If you can just extend the types then you can provide operators that way and there's less opportunity for ambiguity. See Rust.
Also so many of the C++ operator overloads are broken instead of just not existing, which tempts you to overload things instead of saying "Nope, that's a bad idea" and just walking away altogether.
Example, boolean short-circuiting AND and OR. If we write
if (this() || that()) foo();
in C++ then that OR is short-circuiting, so that() won't get called if this() is true.But if the return type used overloads the boolean OR operator, short-circuiting is disabled, and now both this() and that() are always called...