Three Kinds of Concepts

This entry is part of a series, Mapping Concepts: Safety, and Convenience»

When concept support was pulled from C++0x, it was due in part to a controversy about how the feature would play out for real C++ programmers: would they be able to take advantage of concepts without feeling burdened by the introduction of yet another layer of static checking? Unfortunately, it’s hard to draw any definitive conclusions—especially about something as subjective as “usability”—before the feature has been in wide use for several years, and its impact evaluated with respect to both existing and newly-written code. Although it would be ideal to have ConceptC++ in wide use, with a great body of user experience from which to draw, that’s unlikely to happen before concept support is standardized. Instead, we have to make some deductions based on the information available now.

The Three Categories

To understand the impact of concept support on scalability and ease-of-use, it helps to categorize concepts and concept maps according to the roles they play in a generic program. In this short series of articles, we’ll identify these roles and use the distinctions to analyze the effects of using auto and non-auto concepts.

Syntactic Concepts

The simplest category of concepts to understand are nothing but collections of syntactic requirements. For example, a concept called HasPlus represents the requirement that instances of two types can be combined using the + operator:

auto concept HasPlus<typename T, typename U>
{ 
    typename result_type; 
    result_type operator+(const T&, const U&);
} 

The thing that distinguishes syntactic concepts from the other kinds is that they associate no particular meaning (semantics) with the required syntax. Knowing that HasPlus<T,U> is satisfied doesn’t tell us whether T()+U() is a numeric addition, a string concatenation, or causes a tiny robot to tap dance along your keyboard. They also tend to be highly granular, answering questions like, “is there a + operator?” so the size of this category of concepts is bounded by the grammar of the core language.

Syntactic concepts have two main uses that we know of:

  1. constraining templates that represent computational patterns. For example, in C++03 the semantics of std::accumulate are described as follows:

    Effects: Computes its result by initializing the accumulator acc with the initial value init and then modifying it with acc = acc + *i or acc = binary_op(acc, *i) for every iterator i in the range [first, last) in order.

    The “Effects” clause above reads like a procedural description of the algorithm. There’s no mention of whether this represents a numerical accumulation, the concatenation of a collection of strings, or something else (dancing robots). If you compare the specification of most other algorithms (say, nth_element), you can see a distinct difference:

    After nth_element the element in the position pointed to by nth is the element that would be in that position if the whole range were sorted. Also for any iterator i in the range[first, nth) and any iterator j in the range [nth, last) it holds that: !(*i > *j) or comp(*j, *i) == false.

    This clause describes a result, rather than a procedure. For the result to be sensible, the operations involved have to be more than mere syntax: for example, the element comparison has to satisfy the semantics of a StrictWeakOrder.1

  2. constraining components such as the operators in Boost.Lambda, whose sole purpose is syntax adaptation:

    template<typename T> T create_a();
     
    // The function object generated by an expression such as _1 + 42
    template <typename Lambda, typename RHS>
    struct add_right
    {
      template <typename ...Args>
         requires Callable<Lambda, Args...>
           && HasPlus<Callable<Lambda, Args...>::result_type, RHS>
      auto operator()(Args ...args) -> decltype(create_a<Lambda&>()(args...) + create_a<RHS&>())
      {
          return f(args...) + r;
      }
     
      add_right(Lambda r, RHS const& r)
        : f(f), r(r)
     
      Lambda f;
      RHS const& r;
    };

As demonstrated by the “Effects” clause of std::accumulate, syntactic concepts are often associated with generic functions whose documentation essentially exposes implementation details, and as such their use is usually discouraged. Sean Parent has gone so far as to label them “partial concepts,” and the committee has agreed on the HasSyntacticFeature naming convention to distinguish them from concepts with semantic implications.

Foundational Concepts

Foundational concepts have low complexity and a single agreed-upon syntactic structure and semantics. This category includes concepts like CopyAssignable, that capture the usual meaning of core language constructs; like SemiRing, that reflect well-known mathematical ideas;2 and like Swappable, whose operations have broadly-established syntax and conventional meaning.

With the notable exception of algebraic structures such as SemiRing, many foundational concepts known today are already supplied by the standard library. Also, our experience shows that it takes a long time to develop widespread agreement on syntax and semantics for new concepts, so we don’t expect new foundational concepts to proliferate quickly.

Nontrivial Concepts

Any concepts that are neither syntactic or foundational we call nontrivial. Nontrivial concepts have generally higher complexity, and modeling them usually requires significant coding effort. Examples in this category include graph concepts and the standard iterator and container concepts.

We expect a large majority of concepts written outside the standard library to be nontrivial, since:

  • The number of granular syntactic concepts is bounded
  • The standard library already supplies most syntactic concepts
  • The standard library already supplies many foundational concepts
  • New foundational concepts don’t come along every day.

Summary, and What’s Next

This introduction will help us to understand the use cases and benefits of both automatic and explicit conformance. Next in the series we’ll look at three ways of modeling concepts, the three risks of accidental matching, the (three?) benefits of explicit concept maps, and finally, we’ll make some hypotheses about real-world usability.


  1. For the purposes of this article we’ll leave aside whether it is sensible to have an algorithm called “accumulate” that uses the + operator, but that has no semantics other than its pattern of computation. The functional programming community has sensibly chosen the name fold for this pattern. 

  2. Note that SemiRing might not quite fit the criteria today, since there is no widely-accepted C++ syntax for producing the additive and multiplicative identity elements. The usual mathematical syntaxes are “0” and “1” respectively, and a corresponding C++ syntax should be established. User-defined literals and constexpr may make a suitable convention feasible. 

Posted Sunday, August 23rd, 2009 under Concepts.

23 Responses to “Three Kinds of Concepts”

  1. Alex Karpenko says:

    Hi Guys,

    Let me preface this comment by saying that I’ve read all posts with great interest. This is an excellent blog. I’ve learned a great deal from the posts and comments. I’m very excited about C++0x. Type inference, range-based for loops, closures, and initializer lists can’t come soon enough!

    I do have a couple of gripes though that I wanted to rant about. My biggest issue is probably the alternate function syntax. The justification for it seems to be as follows:

    template<typename LHS, typename RHS> 
      decltype(lhs+rhs) AddingFunc(const LHS &lhs, const RHS &rhs) {return lhs + rhs;}

    This is apparently not legal since lhs and rhs have not yet been defined; so the proposed solution is:

    template<typename LHS, typename RHS> 
      [] AddingFunc(const LHS &lhs, const RHS &rhs) -> decltype(lhs+rhs) {return lhs + rhs;}

    When I first read this, I was baffled. Really? Why not just go with the former (saner) syntax and require the compiler to “look ahead” in the function declaration? It’s all one statement anyway! Just look at the function parameters first, then look at the return type. Who cares that it’s typed in the reverse order?!

    This seems akin to the vector<list<int>> issue, which was resolved by having the compiler do “the right thing”(tm) instead of needlessly burdening the programmer. In fact, this is even less of an issue in the code above, since there is no ambiguity. The former statement is sane, while the latter is verbose and adds unneeded complexity to an already complex language. Just great. We take a step forward with uniform initialization, and take one step back with a new way to declare functions. Lovely. Here’s a crazy idea: why not force the few compiler folks to do a little bit of extra work “looking ahead”, instead of forcing the rest of us to suffer the new verbose function syntax until it’s inevitably “fixed” 7 years down the road; much like the vector<list<int>> non-issue.

    My rant got a bit out of hand length-wise, so I’m going to submit the second part on concepts separately. In the meantime I’d love to hear what other folks think of the new function syntax.

      (Quote)
  2. kempozone says:

    Im sure many of you are like me and one of the first things you do in the morning is head here and check out the new post. Along with seeing the new posts, I’m also always checking out the blog roll rss feed and watching them grow, or shrink sometimes. In one of my past …but all in all excellent site. Keep it up!

      (Quote)
  3. Ken Camann says:

    I have an idea for something that would be interesting to see in concepts, but I am not sure how feasible it would be. I’m not sure if there is some hack using the old proposals that could make this work, but I am guessing the answer is no.

    Suppose you have a class that satisfies the “Vector3″ concept, which is a 3 dimensional vector. However, there is another concept called “NormalVector3″ which has the expected length == 1 axiom and it doesn’t let you use +=, -=, etc. to vector addition/subtraction or any other operation that might ruin the “normalness” of the vector.

    Now suppose you create an instance “x” of the “Vector3Impl” implementation class. “Vector3Impl” satisfies the Vector3 concept. Here is a declaration:

    Vector3Impl x(1, 2, 3); // Not a normal vector in general, but it could be!

    The thing is, once you call:

    x.normalize();

    the instance is now guaranteed to be a normal vector, except the method has no way of communicating this to the compiler without creating a new object of a different type that has different semantics. Since the compiler doesn’t know this, if you had a concept-overload like this:

    template <NormalVector3 N>
    void ThisOnlyTakesNormalVectors (const N&);

    as far as I understand, you cannot pass x to it because x has the original type Vector3Impl, which satisfies the concept Vector3 and not NormalVector3. By the way, perhaps that is what the other poster meant when they referred to “concept_cast”?

    Calling normalize changes “*this” but you do not want it to change “this” or return a new object. Instead, you want to say that x has the same type and does move in memory at all, but at compile time conforms to a different semantic concept, that of being a normal vector.

    I wouldn’t use vectors and matrices just for exposition; they occur everywhere in scientific computing, where performance is critical and you can’t copy anything. It would be nice to be able to say “once I modify this l-value, I can now guarantee (or drop a guarantee) that an object belongs to certain overload set, which will change how it is treated by the compiler after the modification point”.

    For example, you tack some syntactic information onto the Vector3Impl::normalize method, so that upon returning it mutates (in some sense) the concept overload resolution of x, so that it can now be passed into the function “ThisOnlyTakesNormalVectors”.

    I don’t know what to call this, or if its a very bad idea in the general case. It is like “instance concepts” instead of “type concepts”. It’s different than dynamic typing because the methods explain at compile time how they are changing the concept of the mutated object.

      (Quote)
    • Sebastian says:

      Concepts is about types and not about states. Overload resolution is done at compile-time without checking some object’s state. Dynamic dispatching (virtual functions) is also restricted to types and not states. If you want dynamic dispatching according to an object’s state someone (you or the compiler directly supporting this language feature) needs to inspect the state (like a boolean flag is_normalized for example) and decide what to do next.

        (Quote)
      • Terje Slettebø says:
        Ken Camann:Calling normalize changes “*this” but you do not want it to change “this” or return a new object. Instead, you want to say that x has the same type and does move in memory at all, but at compile time conforms to a different semantic concept, that of being a normal vector.

        The problem with this approach is that the compiler can’t in general know whether the object has gone through the change or not (because it happens at run-time):

        if(some_user_input())
          x.normalize();
         
        ThisOnlyTakesNormalVectors(x); // Call succeeds?

        However, using move semantics and clever compiler optimisations (such as RVO), you might get away with code like below, with little or no overhead (and no movement of data):

        Vector3Impl x(1, 2, 3);
         
        Vector3Normalized y=x.normalize();
         
        ThisOnlyTakesNormalVectors(y); // Ok
          (Quote)
  4. Jonas Persson says:
    Doug Gregor: Contract programming was discussed in the committee and was ultimately rejected. Axioms were a much narrower feature meant to describe the semantics of concepts; we knew about the link with contract programming, but nobody seemed interested in pushing the idea forward as part of concepts or instead of axioms.No, there’s no notion of “the value previously held”.

    What were the reasons for rejecting contract programming? It is a commonly used, well understood and very useful programming technique that could gain a lot from language support. I can’t see them as a replacement for axioms. They are independent features that could complement each other very well.

      (Quote)
  5. Maxim Yanchenko says:
    Dave Abrahams: C++0x lambdas are actually a big disappointment to me

    I totally agree. But looks like polymorphic lambdas are to appear in the next Standard (hope it’s not C++2x)?

      (Quote)
  6. Doug Gregor says:

    Whew, that’s a lot of questions! I’ll try to answer a few…

    Andrzej Krzemienski: This is a couple of questions that puzzle me.
    1. Are the axioms less stable a feature tahn the concepts? It looked like whereas the great majority of the committee is in favour of concept, it is not so for the axioms. Is it possible that once the concepts make it into the standard, they will make it w/o the axioms?

    There is less experience with axioms than with most of the other pieces of concepts. In particular, there is very little experience in actually using the axioms for anything interesting.

    2. I have seen the following axiom in the standard draft before the removal of concepts: How does the compiler know that we can compare the result of size to 0? The restriction on size_type is that it is IntegralType. IntegralType in tun has many operators, including operator>, but there is no requirement that any of IntegralType be zero, or be comparable to zero. I could easily define a type that matches IntegralType and doesn’t use a zero. In other words, the usage of literals is suspicious.

    Looks like a bug! That 0 should have been size_type(0), since the conversion from integer values to an ArithmeticLike type is marked “explicit”.

    3 . Should constant values not be part of concepts? Trait classes that concepts are supposed to eliminate also defineconstants like EOF, npos, max, (null, 0). This would make axioms more expressive.

    You could use a constexpr function (that takes no arguments) within a concept to provide a constant value, e.g.,

    constexpr T identity();

    often occurs in the concept Monoid to produce the identity element for that type.

    4 . Are axioms only useful inside concepts? They look similar to contract programming facilities like the ones proposed in N1962. And the post/pre-conditions or invariants work well w/o concepts.

    Contract programming was discussed in the committee and was ultimately rejected. Axioms were a much narrower feature meant to describe the semantics of concepts; we knew about the link with contract programming, but nobody seemed interested in pushing the idea forward as part of concepts or instead of axioms.

    5 . Can you specify with an axiom that the result of a move constructor is a value previously held by its argument?

    No, there’s no notion of “the value previously held”.

    6 . Can you specify with an axiom that an operation has no side effects? Or does this axiom:
    Guarantees that add(a, b) has no side effects, if we can somehow prove that a + b has no side effectgs? It is just so little is told about axioms. The only examples in the web are SemiGroup and friends, but this is too little to see the whole picture.

    There is no way to say in an axiom that an operation has no side effects.

      (Quote)
  7. Doug Gregor says:
    Sebastian: DA> Knowing that HasPlus<T,U> is satisfied doesn’t tell us whether T()+U() is a numeric addition It doesn’t even tell us that T()+U() is well-formed even if T and U have default constructors. A satisfier for operator+(T const&, U const&) only needs to accept lvalue parameters. There could be a deleted version taking rvalue references (for no good reason, though). That would be a “hole” in the concepts system that delays an error until instantiation.

    <

    p>Yes, that hole was (intentionally) designed into the concepts system: overloading and selection of specializations occurs at template instantiation time, which means that we could end up picking a deleted overload or a broken specialization, therefore getting one of the dreaded template instantiation errors (ugly instantiation backtrace and all). On the other hand, that late binding allows more rvalue-reference-based optimizations to work with concept-constrained templates, potentially improving performance. This issue was discussed at the committee’s February, 2008 meeting; the end result is that the committee voted for “option #2″ in <a href=”http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2576.pdf rel=”nofollow”>N2576. Prior to that decision, concepts had fewer such type-safety holes.

    Sebastian<rant> I spent a great deal of time figuring out what those function requirements actually mean and how those requirements are satisfied. I think I still don’t get it perfectly. For example: Until recently I was under the impression that when Callable<F,P> is satisfied I could write f(p) in a constrained context where p is an rvalue of type P and f is an rvalue or lvalue of type F and be done with it — even when F takes its parameter by value and I didn’t explicitly require P to be MoveConstructible. Daniel Krügler doesn’t seem to agree with this interpretation. I’m still unsure. What’s the point of std::Callable if it doesn’t guarantee that the call will work? …

    Callable<F, P> guarantees that the call will work assuming you can initialize the P argument. In your example, you’ll get an error at template definition time that says (essentially) that you can’t copy-construct a P. So, add the appropriate constraint to your template and it will type-check.

    Typically, you wouldn’t want to use Callable<F, P> anyway; prefer Callable<F, const P&>. instead, if you’re going to pass it an lvalue that isn’t going to be modified.

      (Quote)
    • Sebastian says:

      I’m not 100% convinced that “hole-free” concept checking and performance are mutually exclusive. Concept checking is done in two places. (1) between constrained templates and archetypes, (2) within concept maps (N2914, section 40.10.2.1). The latter part is described in detail. Function requirements practically turn into expressions that have to be well-formed in order to render the concept-id satisfied. Checking between constrained templates and archetypes is less detailed. You once said that the function signatures are treated like normal functions. Ok, but that really doesn’t match the rules of (2). It’s not only about rvalues and lvalues. It’s generally about conversions and the “hidden parameter type” of a function.

      Example:

      concept Callable<typename F, typename... Args> {
        typename result_type;
        result_type operator()(F &, Args...);
        result_type operator()(F &&, Args...);
      }
      struct A {};
      struct B {operator A() const;};
      struct C {operator B() const;};
      struct T {
        void operator()(A);
      };
       
      template<typename F> requires Callable<F,B>
      void foo(F & f) {
        f( C() );
      }
       
      void bar() {
        T t;
        foo(t);
      }

      As far as I can tell this is another hole with the current rules. The function signature in Callable<F,B> suggests that we can invoke a function with a parameter that is implicitly convertible to B. This includes an object of type C. But C is not directly convertible to A. We would need two user-defined conversions to invoke T::operator() with an object of type C. This seems to result in another late instantiation error. On the other hand this function requirement implies an expression that is guaranteed to be well-formed: We can invoke the function with a parameter of type B that is an rvalue if B is not an lvalue reference. This is an inconsistency. The problem here is that the constrained template doesn’t know anything about whether the function takes actually a B or an A — or a rvalue reference to a B in which case we don’t need B to be MoveConstructible. The actual parameter type is “hidden” but we need to make sure that the object we pass is convertible to this hidden parameter type. But since it’s “hidden” we can’t express such a requirement.

      If we modified the rules for (1) to match the rules for (2) this constrained template would not type-check because Callable<F,B> would only guarantee that an F is callable with an rvalue of B as parameter. We should have written Callable<F,C> instead to make the constrained template well-formed. We just can’t use it with F=T because Callable<T,C> is not satisfied.

      What I’m saying is that I don’t like the inconsistency between the checking rules for (1) and (2). Maybe function signatures are not the right tool for expressing syntactical requirements. Maybe it’s worth trying “expression requirements” again.

        (Quote)
  8. Why would anyone in the real world cares about anything other than getting better errors from template libraries like the STL? That seems to be satisfied well enough by the simple syntax checking case. Heck, static_assert seems like it does that.

    I saw in some older specs that you could specialize templates based on concept? Oh man, that was ridiculous. Who cares? How am I actually going to use that? Then were going to need some kind of concept_cast just to use the libraries. ZOMG.

    I don’t really get what you mean by by constraining boost lambda. I never used that library since it was so ridiculous. It seems especially pointless now that C++ has actual lambda’s.

    If you are arguing for more advanced concept features (I’m kind of unsure) maybe you could give a concrete example of how they would be used in actual application code? Right now the only use I see for concepts is that it saves typing over using static_assert.

      (Quote)
    • Terje Slettebø says:

      “Why would anyone in the real world cares about anything other than getting better errors from template libraries like the STL? That seems to be satisfied well enough by the simple syntax checking case. Heck, static_assert seems like it does that.”

      Short reply: That’s like saying why would anyone care about function signatures? Can’t we just pass in untyped values, and test them against various things inside the function to see if they seem to be compatible with what we want to use them for?

        (Quote)
    • Brendan Miller: Why would anyone in the real world cares about anything other than getting better errors from template libraries like the STL?

      Hi Brendan,

      Thanks for asking.

      I always get nervous around questions about what “anyone in the real world cares about,” since the definition of “real world” is subjective, and although everyone thinks they know what “average C++ programmers” care about, we don’t have any data.
      That said, in the “real world,” some people:

      • would like to write generic programs straightforwardly, without resorting to hacks like tag dispatching
      • would like to know that they have documented all the requirements of their templates without resorting to hacks like concept archetypes; instead they’d like the compiler to check for them automatically
      • would like to have their templates completely checked at the point of definition (similar to above point).
      • would like to be able to use independently-developed types with generic libraries without tedious type wrapping
      That seems to be satisfied well enough by the simple syntax checking case. Heck, static_assert seems like it does that.

      Even if you just care about better error messages for uses of templates, using static_assert to accomplish the same quality of error messages as you could get from concepts would be a lot of work.

      I saw in some older specs that you could specialize templates based on concept? Oh man, that was ridiculous. Who cares? How am I actually going to use that?

      You can write templates that are optimized based on the concepts satisfied by their template parameters. We have plenty of examples of that today (e.g. binary_search works on forward iterators, but is faster on random access iterators) , but today’s examples use the hacks mentioned earlier.

      Then were going to need some kind of concept_cast just to use the libraries.

      Not sure what you have in mind when you say “concept_cast,” but I don’t think anything like that is needed.

      I don’t really get what you mean by by constraining boost lambda. I never used that library since it was so ridiculous.

      Leaving aside the evil late_check feature, any template you want to use inside concept-constrained code also needs to be concept-constrained. So if you want to use a library like Boost.Lambda (or some features of Boost.Bind)—and many people do—in constrained code, that library had better also be constrained.

      It seems especially pointless now that C++ has actual lambda’s.

      C++0x lambdas are actually a big disappointment to me, because unlike Boost.Lambda they’re not (compile-time) polymorphic. I don’t think

      [](foobar const& x, baz const& y) { x +y }

      (or however it’s spelled) can compete with

      _1 + _2

      for usability or readability.

      If you are arguing for more advanced concept features (I’m kind of unsure) maybe you could give a concrete example of how they would be used in actual application code? Right now the only use I see for concepts is that it saves typing over using static_assert.

      I’m not arguing for more advanced concept features, but if you want some examples I suggest perusing the ConceptC++ documentation.

        (Quote)
  9. Sebastian says:

    DA> Knowing that HasPlus<T,U> is satisfied doesn’t tell us whether T()+U() is a numeric addition

    It doesn’t even tell us that T()+U() is well-formed even if T and U have default constructors. A satisfier for operator+(T const&, U const&) only needs to accept lvalue parameters. There could be a deleted version taking rvalue references (for no good reason, though). That would be a “hole” in the concepts system that delays an error until instantiation.

    <rant> I spent a great deal of time figuring out what those function requirements actually mean and how those requirements are satisfied. I think I still don’t get it perfectly. For example: Until recently I was under the impression that when Callable<F,P> is satisfied I could write f(p) in a constrained context where p is an rvalue of type P and f is an rvalue or lvalue of type F and be done with it — even when F takes its parameter by value and I didn’t explicitly require P to be MoveConstructible. Daniel Krügler doesn’t seem to agree with this interpretation. I’m still unsure. What’s the point of std::Callable if it doesn’t guarantee that the call will work? …

    So, long after hearing the rather disappointing news about the removal of concepts, I actually feel relieved by now. Hopefully, we get a chance to play with concepts, fix and simplify them so that average Joe doesn’t need to spend a whole year to understand concepts. </rant>

    Looking forward to other cpp-next articles! :)

      (Quote)
  10. Doug Gregor says:
    Andrzej Krzemienski: Hi. I find it great that there is a place where “ordinary programmers” can talk to language designers. I do not want to go off topic, so just tell me, would it be the right place to ask some questions about concept axioms? Regards

    Welcome! We’re not really setup here for questions, but at some point we’ll probably write an article or two about concept axioms. What in particular are you interested in?

      (Quote)
    • Andrzej Krzemienski says:

      This is a couple of questions that puzzle me.

      1. Are the axioms less stable a feature tahn the concepts? It looked like whereas the great majority of the committee is in favour of concept, it is not so for the axioms. Is it possible that once the concepts make it into the standard, they will make it w/o the axioms?

      2. I have seen the following axiom in the standard draft before the removal of concepts:

      axiom ContainerSize(C c) {
      (begin(c) == end(c)) == empty(c);
      (begin(c) != end(c)) == (size(c) > 0);
      }

      How does the compiler know that we can compare the result of size to 0? The restriction on size_type is that it is IntegralType. IntegralType in tun has many operators, including operator>, but there is no requirement that any of IntegralType be zero, or be comparable to zero. I could easily define a type that matches IntegralType and doesn’t use a zero. In other words, the usage of literals is suspicious.

      3 . Should constant values not be part of concepts? Trait classes that concepts are supposed to eliminate also defineconstants like EOF, npos, max, (null, 0). This would make axioms more expressive.

      4 . Are axioms only useful inside concepts? They look similar to contract programming facilities like the ones proposed in N1962. And the post/pre-conditions or invariants work well w/o concepts.

      5 . Can you specify with an axiom that the result of a move constructor is a value previously held by its argument?

      6 . Can you specify with an axiom that an operation has no side effects? Or does this axiom:

      axiom NoSideEffects( T a, T b ) {
      add(a, b) <=> a + b;
      }

      Guarantees that add(a, b) has no side effects, if we can somehow prove that a + b has no side effectgs?

      It is just so little is told about axioms. The only examples in the web are SemiGroup and friends, but this is too little to see the whole picture.

        (Quote)
  11. Andrzej Krzemienski says:

    Hi. I find it great that there is a place where “ordinary programmers” can talk to language designers. I do not want to go off topic, so just tell me, would it be the right place to ask some questions about concept axioms?

    Regards

      (Quote)
  12. Hoang Vu says:

    I agree. Elements of Programming is a very inspiring book.

      (Quote)
    • Yes, we’re thinking of running some kind of “EOP study group” series here, taking each chapter step-by-step, and even tackling exercises. It’s just not clear what form such a series should take. Suggestions welcome!

        (Quote)
      • Terje Slettebø says:

        Hi Dave.

        The ACCU has quite a lot of experience with study groups like this, through its “Mentored developers projects” (http://accu.org/index.php/aboutus/aboutmentoreddevelopers).

        Often these projects have taken the form of studying a book. Unfortunately, the link to the current projects appears to not work at the moment, but there was a project several years ago, where we studied Andrei Alexandrescu’s “Modern C++ Design” (I found it in the Internet archive: http://web.archive.org/web/20041214015114/www.accu.org/moderncpp/public/).

        The way these projects are commonly done is:

        1. Set up a webpage for it and invite people to register their interest in participating.

        2. When the project is about to start, divide the book into chapter (or sections, if they are large), and assign each chapter/section to a participant.

        3. Set up a schedule for the project (see below for what to schedule for).

        4. When the time has come to their chapter/section, each participant will write a summary of the chapter/section they have read, and invite discussion of it.

        5. There needs to be some way of discussing, either a mailing list, or a system like this site, and I’d think the system here would be just fine.

        6. The question of excercises is an open one, as we don’t have that much experience with that part of such a study group, but it sounds like a good idea, so I, too, invite suggestions on this.

        When scheduling the project, schedule a reasonable time for reading each chapter (include some slack, given that this is done on people’s free time), and then a week or so for discussions after each chapter.

        There needs to be an administrator/leader on these projects, to maintain the website/page, set up a schedule, and prod any people who have yet to deliver their chapter summary… :)

        Here’s one example of such a project that I now found: http://accu-mentored-ejava.blogspot.com/

        The chapter/item summaries doesn’t necessarily have to be this detailed, but just the act of writing a summary helps to internalise the knowledge, as well as helping to initiate discussion.

        This is my suggestion, anyway, and we’ve had several such study groups at ACCU, and it worked quite well. I was an administrator for the “Modern C++ Design” study group (and Andrei Alexandrescu was a mentor for it :) ), but I suggest that you or Doug be the administrator for this one, if you are willing to take it on.

        I’d be happy to participate in such a project. :) Having read more about generic programming, recently, it’s clear that there’s a lot more to this subject than I was previously aware of.

          (Quote)
  13. Terje Slettebø says:

    Hi everyone.

    I’d just like to thank you guys for having started this site for articles and discussions on C++0x and beyond, especially for those of us who can’t or haven’t followed the standardisation work as it has been happening.

    I’d also like to thank you guys for all the hard work you’ve put (and still put) into the development of the C++ language, and especially for larger features like “concepts”, including the development of the ConceptGCC prototype.

    I was fortunate enough to attend a few days at the C++ standards meeting in Lillehammer in 2005, where the two concept proposals (which since have been combined to one) was presented, and this gave me an appreciation of the large amount of work involved in language standards work.

    Due to starting a new job several year ago (developing web applications, not using C++), it has taken my focus somewhat away from C++, but the recent “dramatic” events involving concepts has renewed my interest in the ongoing developments, so I’m happy that a site like this exists.

    On the subject of concepts, I can also mention that several years ago, I developed a library as a kind of experiment into how far you could get with a library-implementation of concepts (similar to BCCL, but allowing overloading on concepts): http://neoscientists.org/~tschwinger/boostdev/concept_traits/libs/concept_traits/doc/. However, it has been clear the whole time that to get the full effect of concepts you really need language support.

    Now on to the actual article this is a comment to: :) Thanks, Dave, for an interesting article on concepts. It seems to me that quite a lot of the discussion on concepts on the Internet is quite confused, talking about how concepts “failed” (when they were yanked from C++0x)… Many years of effort by many people, including reworking large parts of the C++ standard, all disappearing without a trace? I don’t think so! :)

    Also, many don’t seem to appreciate that concepts are about more than syntax (as you give an exposition of here), talking about merely “better error messages”, and how we might get that without concepts… (essentially inverting the stacktrace when reporting it) As your article show, this misses a large part of what concepts are.

    As an aside, I’m currently reading Alexander Stepanov’s “Elements of Programming”, which I warmly recommend (I heard about it from a posting by Bjarne Stroustrup), and which gives good insights into these issues.

    Coming from a computer science background, I don’t have that much education in the field of abstract mathematics, such as abstract algebra, but seeing the connections between this and concepts have inspired me to learn more about it.

    It seems to me that the move from programming with concrete types (e.g. “int”) to programming with concepts is somewhat analogous to the move from elementary algebra to abstract algebra…

    By doing so, we raise the level of abstraction, and is able to prove large classes of “concrete algebras” in the code, so to speak.

    I’d love to hear other people’s view on this.

    Sorry for the long comment; this became almost an article in itself… :)

    Regards,

    Terje

      (Quote)
  14. Maxim Yanchenko says:

    Thanks Dave, that’s useful to know the concepts classification. Waiting for the (C++)Next part!

      (Quote)

Leave a Comment (post replies using links below individual comments)

Spam Protection by WP-SpamFree

Subscribe without commenting