Live data from Hacker News

In defense of complicated programming languages

viralinstruction.com

41–50 of 379 posts

Re: In defense of complicated programming languages

#41
post #17

> In video game design there is a saying: Show locked doors before you show a key This is something I've tried putting into words many times. I'll try to solve a problem and get to know its challenges deeply. Then a tool is introduced that brings it all together. In these cases, I seem to quickly get a full grasp of the operation and essence of the tool. I wish education was based around this principle. A bit like wh…

That's because the use case for classes appears when the information needed to understand the program exceeds the programmer's working memory. At some point, you need some way to make something into a black box whose innards you do not need to understand when not working inside the black box. Languages which do this badly do not scale well.

This is a hard problem. We have, at least, structs, classes, objects, traits, modules, and namespaces. There still isn't consensus on how best to do that.

At a higher level of grouping, we have more trouble. This is the level of DLLs, microservices, and remote APIs. We barely have language for talking about that. We have no word for that level of structure. I sometimes refer to that as the "big object" level. Attempts to formalize that level have led to nightmares such as CORBA, XML schemas, and JSON schemas, and a long history of mostly forgotten interface languages.

It's interesting to read that the author likes Python's new typing system. It seems kind of half-baked to have unenforced type declarations. Optional type declarations, sure, but unenforced ones?

Re: In defense of complicated programming languages

#42
Given their example about classes and "don't show the key before showing the lock", it's ironic the writer pans languages aiming to be "simpler" without understanding the kind of complexity they're actually avoiding. Zig, Go, (and Java and C# and other not as sexy and modern examples) are avoiding C++'s complexity - features with complicated interactions between each other that can sink your program without you being aware at all.

My favourite example is how the integer promotion rules make multiplication of unsigned shorts undefined behaviour, even though unsigned integral numbers have well-defined wrap-around arithmetic. Going by [1], we have the following rules:

1. Usual arithmetic conversions: The arguments of the following arithmetic operators undergo implicit conversions for the purpose of obtaining the common real type, which is the type in which the calculation is performed: (...) binary arithmetic: , /, %, +, -; (...) Both operands undergo integer promotions .

2. If int can represent the entire range of values of the original type (or the range of values of the original bit field), the value is converted to type int

3. (added after the above rules existed for the purposes of better optimizations) If the result of an arithmetic operation on two ints over- or underflows, that is Undefined Behaviour.

4. 0xFFFF 0xFFFF = 0xFFFE001 > INT_MAX

5. Therefore, two unsigned short values cannot be safely multiplied and must be explicitly cast to "unsigned int" each time.

[1] https://en.cppreference.com/w/c/language/conversion

Re: In defense of complicated programming languages

#43

"Classes would still exist, but as implicit patterns." Classes are not a design inevitability, but just one way of managing state. Languages that mostly avoid mutable state don't tend to have object systems, for instance.

> Classes are not a design inevitability, but just one way of managing state What "Classes" are is a tragedy of things. Classes are closures, with a syntactic form that allows for nested composition. This is a particularly brittle form and these qualities are found in all languages with Classes. Language maintainers, as they currently exist, have failed to concede that this is a bad idea, because "it works good enoug…

Classes are closures just as much as closures are classes (as Java showed).

What classes are at their most core is bundles of behavior, possibly with encapsulated state, and usually some way to decide at runtime which of a family of classes' behavior to use. Closures are naturally tied to 1 single behavior (though of course you can invent ad-hoc methods of extending that, such as a "methodName" parameter, but no one ever does this in practice).

This is the reason why classes and interfaces actually exist in some form or another in all useful languages today. For example, Go has structs with methods + interfaces, Rust has structs + traits, OCaml has modules + module types (well, it also has classes, but I understand those are less popular), Haskell has data types and type classes, and on and on, JavaScript has objects + dynamic name resolution.

The one idea from classic OOP that has turned out to mostly be a dead-end is implementation inheritance + subtyping. This is basically the only class feature you'll only find in specific OO concepts, and basically all modern guides tell you to avoid it.

Re: In defense of complicated programming languages

#44

The crucial crux of this is whether the complexity is worth its weight. Different programmers will disagree, not least of which because this is tightly bound to how you prefer to think about programming. Moreover, what seems complex to one person might seem entirely straightforward to another. However, at the end of the day my personal worry is that the central thesis of the article is often inverted. A complicated p…

Interestingly, the strongest bouts of architecture astronautics in the Java ecosystem occurred when the language was simple: lacked generics, functional features, etc. It was the time of EJBs and other such J2EE stuff.

I'd say that feature cohesion is important: in Java, the new(er) features blended into the language without creating incongruities and footguns. In C++, it was not as smooth, though.

Another, and maybe more important factor, is the prevailing tastes in the community. PHP 8 is more complex than PHP 4, but the community attitude changed, many aspects of the stdlib and key frameworks evolved, and now PHP mostly shook off the stigma of being a cluster of antipatterns.

Re: In defense of complicated programming languages

#45

Earlier quoted context omitted.

C++ before 2011 springs to mind. From "Direction for ISO C++"[1]: "C++ is expert-friendly, but it cannot be just expert-friendly without losing important streams of new talent. This was the case before C++11 and C++11 reversed that negative trend." [1]: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2022/p200...

I do rather wish C++ 11 was a blank slate for that reason. Not really, as that'd kill C++, but there's so much good about C++ that is held back by its backwards compatibility with C and the dark ages of pre-11 C++. Data types and features are intended as best practice, but it's stuffed away in libraries since it can't replace the core of the language.

C++ evolution since Rust came along has been amusing. Finally, the C++ designers take safety seriously, now that there's a competitive threat. Until a decade ago, they didn't. I used to argue with some of the committee members over that.

The trouble is, C++ does safety by using templates to paper over the underlying C memory model. This never quite works. The mold always seeps through the wallpaper. Raw pointers are needed for too many APIs.

Re: In defense of complicated programming languages

#46

I remember first encountering classes. I simply could not understand what they were from reading the documentation. Later, I picked op Bjarne's C++ book, read it, and could not figure out what classes were, either. Finally, I obtained a copy of cfront, which translated C++ to C. I typed in some class code, compiled it, and looked at the emitted C code. There was the extra double-secret hidden 'this' parameter. Ding!…

You've never seen tutorials written that way because roughly nobody but you learns programming languages from the bottom up. There is just no demand.

By the way, where can I read a D tutorial from the bottom up?

Re: In defense of complicated programming languages

#47

I remember first encountering classes. I simply could not understand what they were from reading the documentation. Later, I picked op Bjarne's C++ book, read it, and could not figure out what classes were, either. Finally, I obtained a copy of cfront, which translated C++ to C. I typed in some class code, compiled it, and looked at the emitted C code. There was the extra double-secret hidden 'this' parameter. Ding!…

You’re very old school… I love it.

Edit: I just realized from the sister comment who I was replying to. Old school charm for sure. Now I love it even more.

Re: In defense of complicated programming languages

#48
I agree with almost everything about this article. I am also a known Go zealot. What gives?

Well, I think the author underestimates just how much needs differ across different programs. Go excels with services because they are often written with a shared-nothing mindset for horizontal scalability. If you’re using S3, Redis, PostgreSQL and RabbitMQ for all of your persistence and coordination, it leaves only small bits for your service to be concerned about w.r.t. local data races, and they are mostly encapsulated into libraries, sometimes provided by the language, that can be aided with static checking.

There is something in video games called minmaxing, which is where you maximize one attribute at the expense of all of the others. For me C++, and now Rust, feel like they are guilty of this with their weight on zero-cost abstractions. I am not against efficient code. It is super awesome to generate efficient JSON serialization code at compile time. In aggregate, even a few hundred cycles per request could be a big difference.

But as many have already stated, the secret to zero cost abstractions is that it’s zero runtime cost. Just as reducing programming language complexity doesn’t remove it, but only moves it around, removing runtime cost doesn’t remove it either. And the problem here isn’t that compile time isn’t a better time than runtime; it’s the hidden costs that suck. Sure, maybe the compile times in Rust will improve, and maybe it’s not even that big of a deal. But every little feature the language has puts some additional stress on the ecosystem. The language server has to bear this load, for example.

Everyone knows the downsides in Go’s approaches to code generation, but I actually think it’s kind of intriguing. go:generate is imperfect in almost every way, and yet it’s oddly practical. It doesn’t guarantee code is not stale, but clever codegen design can cause syntax errors when regenerating code is needed. But maybe most interestingly, gopls doesn’t need to care. The go compiler doesn’t need to care. The parser doesn’t need to care. As far as they are all concerned, the generated code is just more code. To me, this is clearly an approach that doesn’t suffer from minmaxing. Maybe it suffers from a dogmatic fetishized idea of simplicity, but I don’t think so: I think it was literally just meant to be the bare minimum to try to solve the most important parts of the problem, and at that it succeeds.

I am not saying that Rust isn’t awesome. In fact, what I am saying is that I think a lot of smart people have fallen into the trap of thinking that Rust is the one true programming language simply because it’s such an alluring story. But, I think there’s more room. Formally verified C, Go, Rust and Zig will probably all co-exist for the foreseeable future, along with many other permutations of programming languages. Nobody is going to write a web browser in Go, or at least not anything to the scale of Servo or Chromium. But, if you wanted a language to write some servers in, well, hell, you could certainly do a lot worse than Go.

I don’t want to go too into it, but I also think there is some overly optimistic viewpoints here. It is true that reducing language complexity will often move it elsewhere, but that does not mean that language features do not cause complexity that doesn’t exist otherwise. For example, the Rust borrow checker. Yes, I love it. It is not perfect. It allows only a subset of valid, correct code, and it’s excessively hard to write, for example, a correct doubly linked list in Rust. Does that mean it’s useless? No. But, in general, it’s not very hard to write a correct linked list. (It’s easier to write a subtly flawed one, but that’s neither here nor there. :) Thankfully, most people do not need to write a linked list implementation, but I do still think this stands as an interesting illustration nonetheless.

And I am not suggesting that it’s a superior option at all, but if you are in a simple language like Go, it’s still possible to eat some cake and have it, too. gvisor’s checklock program is a pretty good example. Not perfect in almost any regard, but again, it represents a different set of tradeoffs.

Rust frontloads an awful lot of complexity for cases that are not necessarily common for everyone. And that complexity, both in the toolchain and the resulting code, is a cost that gets paid repeatedly forever. It needs to have a good payoff. Systems programming and game developers have a lot to gain. Everyone else? I think it’s up for debate.

May the best languages prosper. Ideally cooperatively.

Re: In defense of complicated programming languages

#49
post #17

> In video game design there is a saying: Show locked doors before you show a key This is something I've tried putting into words many times. I'll try to solve a problem and get to know its challenges deeply. Then a tool is introduced that brings it all together. In these cases, I seem to quickly get a full grasp of the operation and essence of the tool. I wish education was based around this principle. A bit like wh…

Very quotable principle. Any attribution?

Re: In defense of complicated programming languages

#50

A language with a short learning curve is like a toolbox that’s nearly empty. You quickly run out of ways it could help you. We should optimize for experts, because that’s where each of us is going to spend most of his career.

> A language with a short learning curve is like a toolbox that’s nearly empty. You quickly run out of ways it could help you. Very quotable! That's why I like it when languages allow you to start writing simple code and gradually make it more complex. Haskell for example is not like that. Python does better here. I think Scala is one of the best languages in that regard.

> Haskell for example is not like that. Python does better here. I think Scala is one of the best languages in that regard.

This seems ripe for getting a lot of passionate anecdotes out of the woodwork.

Post reply on HN