Hacker Newsnew | past | comments | ask | show | jobs | submitlogin
Why I want Concepts, sooner rather than later (isocpp.org)
61 points by vmorgulis on Feb 20, 2016 | hide | past | favorite | 30 comments


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.


If 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?


My first thought is, why exactly do you think everyone concluded that OOP classes and inheritance is bad architecture?


It'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/08a2c0b7dd1a8950e... Good times.)

Name the use cases where you think inheritance is a good fit.


Inheritance 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.


Yeah widget toolkits (if not done across maintenance boundaries) are really the one situation which is always described as a good fit for inheritance. However I don't think I like the idea of "close coupling across module boundaries". Sounds like an antithesis to sustainable architecture to me. Anyway I don't do UI...


It's just a tool. OOP is basically the same as

  Classname_method(&structure, ...)
Inheritance is a good fit when you want execution efficiency when implementing interfaces. It makes you think in terms of base behavior vs customization and thereby re-use code properly. Most of the time, the resul is that the base class implementation is generic enough to do what you need regardless of the specific instance. But if you really need to override base behavior, that's when you can have things layered on top of basic inheritance.

For example:

* Database access (ORM or NoSql)

* Any sort of server object access (eg File, Request, Socket)

* APIs that discourage duck typing to increase cohesion

* Low level implementations that need efficient implementations of function calls

* Automatic, zero-overhead memory management http://www.stroustrup.com/resource-model.pdf


Duh. that paper is... long. And it mentions inheritance 0 times.

Maybe compared to duck typing inheritance increases cohesion. But classes in practice most often discourage cohesion. That's why they came up with Aspect-oriented programming. Not that that is practical, but it comes from the insight that organisation as stateful objects naturally leads to coupling, since you must lump most functionality into one mostly closed concept. And the odds are your next class will have a very similar structure regarding dependencies and implementation. And then you end up with inheritance just to share code, and end up with these totally meaningless ball-of-mud inheritance structures.

> Low level implementations that need efficient implementations of function calls

I'm pretty sure the most efficient "implementation" of function calls are still function calls.


I meant method calls


Are those programmers Java or C# programmers?

I'm not an OOP expert, but I get the impression that type classes are similar to interfaces in Java and C#, which are used all the time in those languages. If you told a C# programmer that IEnumerable was an example of bad architecture, I think you may get a somewhat different response.


Agreed. I didn't claim that interfaces were bad architecture (but actually I've often seen it overdone).


A drawback of inheritance is strong coupling.


Or 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.


Meanwhile, you can use Rust now where traits (which fill a very similar niche to concepts) have been available for a long time, and are widely used in the standard library.

Of course, once you do, you find that some of the traits you would like to be able to use aren't expressible without "higher kinded types" (https://github.com/rust-lang/rfcs/issues/324), so there are still some big features that everyone kind of hopes will happen soon but there's no current plan for. But the existing trait system gets you pretty far for a lot of what you want to do.


Traits are not like Concepts. Concepts are for template meta-programming. You're thinking of the "Contracts" proposal which would be like concepts but for normal types, so like traits.

Also, can we have a C++ article where the comment section doesn't turn into a Rust ad?


You just reached the Rustwin's point of Hackernews: "As a discussion about C++ or C grows longer, the probability of a comparison involving Rust, as a savior, approaches 1. Afterwards any further discussion is considered null and void".


We could if C++ wasn't so complicated, overwhelming, and prone to writing bad code that everyone feels compelled to replace it and going as far as starting a project like Rust to solve the issues Python or Go do for C++ with the same level of zero-cost abstractions.

Devil's Advocate: C++ may be complicated, but try comparing hello worlds in each language. Maybe Rust still isn't the holy grail either.


Rust:

  fn main() {
      println!("Hello, world!");
  }
C++:

  #include <iostream>
  
  int main() {
    std::cout << "Hello, world!";
  }


The Rust version adds a newline (hence println) while the C++ version doesn't. Either the Rust version should use `print!`, or the C++ version needs an `<< std::endl`


Adding '\n' to the string is perfectly fine, no std::endl needed (\n translates to the system-specific newline and the stream is flushed on destruction anyways).


And this was published in response: "Why I want Concepts, but why they should come later rather than sooner" [0]

[0] http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p024...


It's all about timing, with C++11/C++14 having changed the language substatially it would be nice to fix some of the issues that templates have caused over the years. Due to new behaviours on the language existing templates might stop working.

Honestly almost any template by itself it's brittle and can fail to execute as predicted depending on the type received. This causes major issues and has to be solved sooner rather than later.

Also important to note that Concepts went through a lot of work and it evolved from concepts to the current concepts lite that was proposed for C++17.

I would like that the committee was a bit more open when mature proposals backed by senior members fail to pass. It could help other people understand the issue and maybe work towards solving it.


Can someone explain what C++ Concepts are and what they're used for? I see they'd simplify the code greatly but when I went to read this article I expected him to talk about maybe an approach to programming that focuses on the concepts first or something and was completely surprised that this is a programming feature.


In essence, they are used to allow template writers to easily put constraints on the type parameters that their templates require, and provide better diagnostics to users of template classes when they try to instantiate a template with the wrong parameters.

Normally when you wrongly instantiate a template you get a block of text which describes exactly what went wrong. In terms of the language as it is now, the whole block is necessary, but the ability to mentally parse it is a skill that requires time and is otherwise relatively useless. In addition, for library users, errors could happen way inside the internals of the library, and the users would have little to no information as on how to parse such errors.

To try to mitigate this, type traits were introduced. These are used by template writers, and their task is to check that template parameters satisfy certain given requirements or else a certain overload is discarded (using SFINAE for example). In this way, errors would be caught before trying to actually instantiate a template, and users would not be exposed to errors within the internals, but just at a boundary when a template was being instantiated. But still this approach has limitations, and the errors are still not quite pretty.

Concepts tries to fix all this with an approach that resembles java's "implements". It provides a way to easily specify and check at compile time the interface required by template parameters, so that it can be checked before instantiating the template and give better errors in terms of the concept rather than type traits features.


This notion is introduced in a pedagogical way here http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2013/n358...


Quite frankly, I still don't get it. Concepts are a big, complex feature and the main thing you get out of them is that it makes it easier to write error-checking for template parameters. I don't think I've ever actually been bitten by that problem in the first place.

I was champing at the bit to get C++11 features like auto, move, lambdas, unique_ptr, bind and random. I could see exactly where I'd use them to make my code simpler.

The C++17 proposals mostly are uninspiring in comparison. Some seem minimally useful while others seem very complicated. I worry that we might end up standardizing a bunch of useless or poorly considered features.


I think everyone who programs in C++ has had their terminal scrollback buffer be wiped by a giant and entirely useless error message from the compiler due to some template substitution error.

Even if you don't define templated types yourself (very common), you will use them all the time, and if concepts lead to better error messages, then it'd be a win for everyone who uses libraries like the STL or Boost.


>I don't think I've ever actually been bitten by that problem in the first place.

That I think qualifies you to be a few of a kind if not one of a kind. If you use a lot of templates and manage to avoid the problem of 'late errors' with a reasonable effort I would love to know more about your programming style


I suppose I probably have been bitten by this sort of thing when using iostreams and Google Mock.


Summary: The article is talking about including Concepts in the upcoming C++17 standard - but only in the language specification, not the standard library because it's not fully ready. It argues that the language part is ready, though.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: