4 ms·
I was involved in putting concepts into the g++ standard library, for the aborted C++0x concept design. My feeling is this is a classic example of an area wher
by CJefferson 11y ago
I was involved in putting concepts into the g++ standard library, for the aborted C++0x concept design.
My feeling is this is a classic example of an area where the perfect is the enemy of the good.
At one extreme we have C++'s current design -- try substituting the types into the template and see what explodes. At the other extreme (which is what C++0x templates did) is that if a template passes 'concept check', we know, 100%(ish), that the resulting code is going to compile.
The problem is, that this means when you write the concepts for a function, you have to have concepts for every corner of the C++ language you could make use of. This means for many functions the concept list ended up being longer than the function! Further, it is easy to miss optimisations (such as rvalue references), because you didn't 'concept' for them.
The latest aim is (to me) a good middle-ground. It checks (for example) that something is "numberish", or "iteratorish". You can easily create freakish types which will pass concept check, and then not compile, leading to the kind of error messages users currently get. But the common cases, of people passing sensible, but wrong, things into methods, will get much nicer errors.
- jstimpfle 11y agoIf I get the idea, concepts are similar to Haskell's decade-old type classes, as an alternative to OOP classes and inheritance, which by now almost everybody has agreed are largely a bad idea from an architectural perspective. Did anyone see Edward Kmett's talk "Type classes vs the world" and wants to lose a few words on how they compare? Regarding the problem you mentioned, I've often tried to break my problem domain in "abstract concepts" using templates, static assertions, code generation etc. I don't think I have a real word success story. Abstraction doesn't work out of course for business logic because generalizing over the mess which our world is is just incredibly hard, but also not for algorithms, because quite frankly, efficient implementation is the result from learning a thousand neat little tricks and applying them in the right situations. Abstraction remove these opportunities. For example, recently I've seen a Java application with a parameterized container class. The parameters were abstract and thus the container had to use tons of nested Hashmaps to get from A to B. Had it admitted that the only sensible parameter was "int", everything could have been implemented using a few super-fast arrays. Another problem is that abstraction actually grows cancerours dependencies across the code base. If we instead design clean data interfaces reusing basic data types and add a few lines of comments / constraints / consistency checks, this dependency hell just freezes down and we notice that abstraction wasn't the actual problem to begin with. For the basic quick and dirty app, or script, of course it's extremely important to have generic STL containers and a std:: algorithm, or python dicts / set / iterators, and be done with it. Also in rare cases, abstractions like Monoid, Functor, Applicative, Traversable, Monad can come in handy when you can reuse a few lines of code from a generic library. But to come back to your point, I think that's all largely explored territory and the point of diminishing returns has long been surpassed. Thoughts? Counterexamples?
- EGreg 11y agoMy first thought is, why exactly do you think everyone concluded that OOP classes and inheritance is bad architecture?
- jstimpfle 11y agoIt's my impression -- I've heard it from so many programmers that I respect. The impression may be biased by my own frustrations dealing with bad abstractions (like: 90% of the classes I've seen out there are semantically broken, a.k.a not state machines). (To back that up, there is a python script of mine which I think is rather clean. It has basically a single class in it, which was needed because the API of cmd.Cmd relies on inheritance. I had to spend quite some time to circumvent that insane amount of brokenness. I've put in a few cynic comments out of frustration. If you're interested: https://github.com/jstimpfle/gitadmin/blob/08a2c0b7dd1a8950e3e9084a964ae3a248b29b39/gitadmin#L1303 https://github.com/jstimpfle/gitadmin/blob/08a2c0b7dd1a8950e... Good times.) Name the use cases where you think inheritance is a good fit.
- millstone 11y agoInheritance got a bad rap in part because object oriented analysis was done as an exercise in taxonomy (Employee is a Person is a Mammal is an Organism...), instead of identifying useful abstractions. A second problem is using inheritance when composition is obviously better. java.util.Stack is a lovely example of both sins in action. Where inheritance shines is in enabling close coupling across module boundaries. An example is event routing: how do you enable customization of how events flow through your app? UIKit handles this through inheritance. The base method gives you the default behavior, which you can override to customize. You can stop propagation by just not calling super, or redirect the event by calling the method somewhere else, etc. It's flexible. JavaScript handles this clumsily, by trying to anticipate all the customizations you might want to do and providing APIs for each of them (event.preventDefault, event.stopPropagation...) Despite all that, frameworks like React have to reimplement event dispatch, because the default behavior is too inflexible. Here inheritance is the glue that enabled close coupling between UIKit's default event dispatch algorithm and outside modifications to it.
- anon4 11y agoOr you can do what I've seen every large C++ project do and not use templates other than for simple containers and wrapper pointer types.