Hacker News new | ask | show | jobs
by dasloop 2716 days ago
Probably because you are used to the paralysed C++ pre C++11. Nothing happens for years and now we have new functionalities every three years supported by all major compilers. But, also, many new functionalities are not for global consumption. The audience of many meta programming functionalities are library developers for example.
2 comments

Isn't every developer in a non-trivial software project a library developer? As soon as you have a common piece of functionality you want to reuse - that's a library.
I think that's dependent on language culture. Because the C++ community doesn't take language simplicity or comprehensibility seriously, there are lots of C++ developers who can't use or reason about surprisingly large parts of the language. So the community has rallied around the notion that "library" developers need to understand everything and that most developers will just glue together bits that the library devs made.

I mean, how many C++ developers actually write serious template code? How many of them could reliably explain what the keywords in post do?

The idea that every developer is a library author (or the lisp extension that every developer is a language author) is common in many other language communities but it relies on the community working hard to make mastery of the language feasible for lots of people. The C++ community never bought into that notion; they inherited a very stratified class structure from Bell Labs.

>... the C++ community doesn't take language simplicity or comprehensibility seriously...

I think this is an unreasonable assertion and not borne our by a read of the committee discussions.

To clarify: of course they like simplicity when it costs nothing. But they consistently value other goods over simplicity.

For example: maintaining backwards compatibility. The community believes that it is more important that 20 year old C++ code run unmodified than that the language should be simplified. There's lots of stuff you could do to simplify the language but options dry up in a world where 20 year old code must be able to run unmodified.

So sure, the committee talks a lot about simplicity, but it isn't willing to sacrifice much.

Don't get me wrong: I'm glad that finally, in 2020, C++ will be almost but not quite as good as Common Lisp was at metaprogramming back in 1982. But it remains the case that eval-when and defmacro are both more powerful and dramatically simpler than anything the C++ committee has ever considered.

Two other goals for C++ are 'zero-cost abstractions' and 'leaving no room for a lower level language'. It does better on both of these goals than Common Lisp and they are important reasons for its popularity (along with backwards compatibility and easy interop with C APIs).
Zero-cost abstractions only exist in a world where you don't highly value language simplicity and comprehensibility.

Simplicity and comprehensibility were things the committee had to give up in order to pretend they had "zero-cost" abstractions. Nothing in life comes free: everything, including all abstractions, comes at some cost.

> For example: maintaining backwards compatibility. The community believes that it is more important that 20 year old C++ code run unmodified than that the language should be simplified. There's lots of stuff you could do to simplify the language but options dry up in a world where 20 year old code must be able to run unmodified.

This pains me but every time I think "just toss XXX out, gddammit!" I think of IPv6. C++11 is still the most popular dialect of C++, even for new development I believe, and c++14 is the hot new thing to many people.

> Don't get me wrong: I'm glad that finally, in 2020, C++ will be almost but not quite as good as Common Lisp was at metaprogramming back in 1982. But it remains the case that eval-when and defmacro are both more powerful and dramatically simpler than anything the C++ committee has ever considered.

C++ is held back by having statements. If the basic structure were an expression a lot of programming, much less metaprogramming, would be simpler.

> Isn't every developer in a non-trivial software project a library developer? As soon as you have a common piece of functionality you want to reuse - that's a library.

A library, specially a C++ library, is way more than reusable code. Developing a library requires the developer to spend time making fundamental design decisions that he doesn't have to make when developing a module lost somewhere within a project tree, such as how to organize the project into interface and private source files, how the lib should be deployed, how to meet upstream dependencies, how to not break compatibility with previous releases while making your code resilient to subsequent changes, how to add metadata to your project, how to handle optional features, etc etc etc.

I understand this sentiment but think it is problematic. In fact, I think it is part of the reason a lot of libraries aren't very good.

Working in a large project, as you note, naturally leads to many conversations about sharing code and/or functionality. So you bundle something together or add an access point, call it "library X" and you are done, right? Any problems can be patched around later as you are working on a common base.

To me, this is missing > 50% of the work in designing and delivering a library for general use, which is why it often causes problems when you treat it as "done".

Which isn't to say this isn't the right thing to do in your situation! It's just that this is vastly different than what someone might be talking about in "library developer". It's not prima facie crazy to have language features mostly aimed at the latter, if it's a language often used for it. Which, for better or worse, c++ (still) is.

It wasn't my intention to say that writing a library in C++ is trivial. Just that it's inevitable in non-trivial projects. So I agree that lot's of knowledge and thought have to go into API/ABI design, versioning etc. On the other hand, only because C++ gives you lots of choices that other languages don't, it doesn't mean that only a hand full of "library developers" will have to make those choices. Almost everybody has to make them, coincidentally or consciously.
I guess there are different levels of libraries. Something like boost is a different thing from encapsulating business logic into a library.
And that's exactly what's different in D, where the audience for meta-programming is "everyone". It's not just about more power, but how accessible this power is. To think you need to suffer to have this power is not true.
D vs. C++ in this list of examples looked equivalently complex and equivalently power-user focused. static if not introducing a new scope seems like the sort of confusing, error-prone edge case that trips up newcomers, for example. We are taught very early on that in C-style languages curly braces means a scope. Except here in D in this particular case for some reason it's not that isn't clear why until you are very deep into understanding the language.

Similarly operator overloading via a string that tells you what you are overloading seems... insane? Very error-prone & complex?

Not that C++ is great here or anything, but it seems disingenuous to claim D's complex thing is for everyone while C++'s nearly equally complex thing is too complex for everyone.

> Similarly operator overloading via a string that tells you what you are overloading seems... insane? Very error-prone & complex?

Frankly you should try D for just 5 minutes and see for yourself, because no it is really sane and works well. Never seen anyone complain about this...

See here it is used to implement all operators for small vectors in 46 lines: https://github.com/d-gamedev-team/gfm/blob/master/math/gfm/m...

It appears to only be primarily useful if you are writing a pure wrapper class where you proceed to delegate to an actual implementation.

But you could still do that and not be string-based. It could (and should!) be an enum of the operator instead. opBinary takes a fixed number of ops, but the parameter type of string has an infinite number of values.

Whether or not the design of having a single operator overload method is a good idea or not is independent from what I'm specifically calling insane which is that the parameter type to that method is a string.

If it is string-based meta-programming you find ugly (as many do as first), consider the alternatives are maybe not much better in practice.

https://forum.dlang.org/post/l5srs7$2m3$1@digitalmars.com

Nothing stops "everyone" from using the features which suit library developers.
Indeed, but as a large, general-purpose system programming language there are many features that support certain important and special cases, with no application of the language, even library development, using them all.

As an example, we needed locked containers, so wrote a little template and specialized it over the couple of containers we needed. It supported just what we needed. If this same functionality were extended to the standard container library it would not only have need to be thought out to handle every non-locking case, but would have either needed a lot of repetitive boilerplate (and repetitive specializations) or else additional hair that was not worth our while to learn/use. We were able to avoid the problem by adding some documentation in the local style guide.

Except ever increasing time to learn all the language features and associated best practices.
While true on the face of it: I haven't had the need to learn many other languages so I haven't had the need to learn the best practices of them.

Use what you need, learn what you need. Don't pay for what you don't use :)

You can't in C++ because learning the language takes 10 years and is a never-ending story.
Any true language is ever evolving, even spoken languages.
Not all languages are the same, C++ is vastly more complex than many other programming languages (arguably all of them). The only reason to keep learning C++ after years of practice is the sunken cost.
On the other hand, templates get abused on D as workaround to avoid writing attribute boilerplate across all functions definitions.