capr / blag

:suspect: Blog/wiki of a grumpy old programmer
4 stars 0 forks source link

Your programming language sucks #20

Open capr opened 3 years ago

capr commented 3 years ago

Paradigm

Your language sucks because it's either high-level or low-level but can't do both. This is why a lot of complex programs (games, CAD software, etc.) need to use two languages: one for the engine and one for the application logic and UI, and spend a lot of time having them talk to each other.

Apparently, language designers today (with rare exceptions just can't imagine a language that can have both typed arrays and untyped arrays, both fixed-layout structs that can stay on the stack and hash maps that need to be on the heap, both statically compiled code and interpreted or JIT-ed code in the same runtime and that can call each other, both automatic memory tracking and manual memory tracking and choosing between them for each allocated entity. Instead of all that, what we have today is East Germany and West Germany separated by the barbed-wire fence that is the C ABI.

Garbage collection

Your language sucks because it either has a GC or it doesn't have a GC when in reality I need both.

Your "system" language sucks because it can't add two strings together, so it may be fit to write "systems" (whatever that is) but it's certainly not fit to write applications.

Your GC'ed language sucks because it doesn't let me write libraries that can be used in other languages that allocate memory differently, it sucks because I can't program small chips with little memory with it, it sucks because its GC is global so I can't make different parts of my program use different GC arenas, it sucks because its GC is not programmable so I can't control when it runs, and finally it sucks because the GC is not extensible so I can't make it track objects that were allocated outside of it.

Exceptions

Your language sucks because it doesn't have a way to propagate errors down the call stack and catch them later and unwind the stack.

Because your language doesn't have exceptions, all my networking code written in your language sucks. Now I cannot write a web server that closes the connection automatically and frees the memory associated with it when I/O fails, and handle that in a single place. Instead, I have to propagate the error condition myself manually through the entire network, protocol and application code.

Exceptions are almost never needed in a program, I'm down with that. But just because Java created a culture of exceptions everywhere, just because C++ botched it with RAII, doesn't mean that you have to throw out the baby with the bathwater. In networking code, the pattern of recoverable failure which is what exceptions are addressing, is the optimal coding pattern. You'll have resource leaks, you say? Figure it out so you don't. It doesn't have to be RAII and it doesn't have to involve the heap.

RTTI

Because your language doesn't have an API (or data format) that lets me know struct layouts at runtime, I cannot make myself a generic debugger with custom data inspectors like this one or this one without having to implement half a compiler first.

Virtual properties

Because your language doesn't have a way to virtualize field access, all my UI code written in your langauge sucks because I have to write x = a.get_this() and b.set_that(y) instead of x = a.this and b.that = y.

Operator overloading

Because your language doesn't have operator overloading, all my geometry code written in your language sucks because I have to write matrix_add(a, matrix_multiply(b, c)) instead of simply a + b * c.

Fixed-layout structs and arrays

Your dynamic language sucks because it doesn't have fixed-layout structs and arrays that can be allocated on the stack. Because it doesn't have those, all my geometry code written in your language sucks because now I have to make all my APIs return points and matrices in output params or globals instead of in the stack. And no, "allocation sinking optimization" is not a solution because it's unreliable (like most JIT optimizations).

Closures

This can actually mean 2 things: 1) inner functions i.e. functions declared inside a code scope that can use variables from the outer scope; these functions can only be called from within the block in which they were created (GCC has them) and are mostly just useful for writing inner helper functions (still very useful), or 2) first-class functions, which can be called at any time (and have pointers taken to them), but they need to keep some memory for captured variables, so they necessarily must be dealt with as memory objects, which makes them clunky in non-gc'ed languages because you need to surface the fact that they are memory objects. None of that is a good reason not to have closures in your low-level language though. Without closures, callbacks suck. And when callbacks suck, libraries suck.

Meta-programming

Your language sucks because your compiler is not programmable. Because your compiler is not programmable,

Coroutines

Because your language lacks coroutines, my server code written in your language sucks.

Like exceptions, coroutines are almost never needed in a program, except in one case where they are invaluable: writing scalable network software because they can be used as a way to multiplex I/O with low switching-overhead, compared to preemptive threads which have a high switching-overhead. For this specific task, anything less than coroutines sucks: async/await, promises, one-socket-per-thread, callbacks.

Also, I hate to have to mention the obvious here, but the so-called "stackless coroutines" you hear about in some languages are not really coroutines. A better name for those would probably be "crippled, useless coroutines that we did because our compiler backend that does the actual heavy lifting for our language, i.e. LLVM doesn't support multiple program stacks".

Variable shadowing

This is a minor point, but I'll mention it because it comes out of a way of thinking which I particularly loathe: crippling the language intentionally or refusing to put in a language feature because it makes code "hard to read" or its usage can "lead to bugs" and other nonsense like that. Variable shadowing allows you to re-declare a variable in the same scope. Because your language throws an error when I attempt to do that, now I have to invent a new name for a variable just because your compiler forces me to. And I'm not going to list here the use cases for var shadowing because 1) you already failed at the principle level, and 2) you should be well acquainted with those use cases if you want to call yourself a language designer.

Conclusion

In conclusion, your programming language sucks because it's opinionated. Because you call yourself a language designer first instead of calling yourself a programmer first. Because you think you have a duty to protect the users from themselves. Because you think you have to penalize bad behavior and encourage good behavior. Because you don't see the value in a language feature because you haven't used it yourself. Because you're a systems programmer who doesn't understand the value of dynamic languages. Because you're a game programmer who thinks there's a single axis of complexity and you cannot imagine something being more complex than a game, but in a different way in which a game is not.

"When you try to restrict what people can easily do, you will FAIL." -- Linus Torvalds, 2003


I would also refer you to https://wiki.theory.org/YourLanguageSucks but that page sucks.