P1199R0
Mike Spertus, Symantec
mike_spertus@symantec.com
2018-08-10
Audience: Evolution Working Group
	
	A simple proposal for unifying generic and object-oriented programming
 
	In this paper, we propose a simple extension to P1141R1
	that we believe provides a simple and comprehensive proposal for unifying generic and object-oriented programming.
	The problem
	C++ works best when compile-time and run-time programming are similar. For example, constexpr functions allow programs to easily move calculations back and forth between compile-time and run-time as circumstances dictate with no change in code. Likewise, it is well-known that generic programming and object-oriented programming can be applied to similar problems (e.g., Bjarne Stroustrup's Concepts for C++1y: The Challenge), but they do so with very different notations. 
    Once the design decision is made about whether to use templates
	or virtual functions, it is extremely committal and cannot easily be changed.
	For example, in designing a drawing library, one would need to decide whether to provide distinct shapes
        via a Shape base class providing runtime dispatch with virtual
	functions or a Shape concept providing compile-time dispatch.
        If Shape is a class, a function might be declared as
	| boolis_convex(Shape const&s);
 | 
However, it Shape is a concept, a very different declaration must be used:
	| template<typenameT> requires Shape<T> boolis_convex(T const&s);
 | 
Once this choice is made, the programmer is fully committed to one of two very different programming
    paradigms with complex tradeoffs from day one.
	Terse notation, as proposed most recently in P1141R1, reduces the notational gap between object-oriented and template programming:
    
| boolis_convex(Shape auto const&s);
 | 
	However, switching Shape between a class and a concept still requires changing the 
    declaration of is_convex, and even if the declaration were the same (as in the 
    Concepts TS),
    many changes would undoubtedly need to be made to the function body to accommodate changing
    Shape from a class to a concept. E.g., | boolis_convex(Shape const&s) 
 {
   pair<Shape, int> p(s, 7);
   auto x = make_unique<optional<Shape>>(convex_hull(s));
   
 }
 | 
In the end, the obvious similarity between compile-time and runtime dispatch
sometimes feels more like something that is there to tantalize and frustrate us than something
that can be effectively leveraged.
Our Solution
So, how do we create code that works equally well regardless of whether Shape is a class
    or a concept? Fortunately,
	 P1141R1 provides auto as a sigil that indicates something that looks like a type 
    could be a concept.
	| boolis_convex(Shape auto const&s) { }
 | 
	
P1141R1 defines what this means when Shape is a concept. If we want this code
    to work regardless of whether Shape is a concept or a class, we 
	need to define what it means when Shape is a class:
First, we introduce one notation. It C is a class, we let
    inherits_from_C<T> denote the concept asserting that T publicly
    inherits from C.
    For example, if Shape is a class, then inherits_from_Shape is the concept
    
| template<typenameT> concept boolinherits_from_Shape = is_convertible_v<T *, Shape *>;
 | 
Now we can state our proposal simply as:
Proposal
With the above notation, we propose everything as in P1141R1 with the additional rules that if C
is a class,
- Occurrences of C auto are replaced by inherits_from_C auto.
- Implicit conversions
- If a function template has a C auto in its parameter list, any occurrences of C within the function body are also replaced by inherits_from_C auto
That's it! Let's make it concrete illustrative examplesIllustrative examples
As noted above, P1141R1 explains what
| boolis_convex(Shape auto const&s);
 | 
means if Shape is a concept. What we propose is that if 
Shape is a class, then the above is rewritten as
| boolis_convex(inherits_from_Shape auto const&s);
 | 
In this way, we ensure that the declaration of is_convex makes sense
regardless of whether Shape is a class or a concept. Furthermore, since
all occurrences of Shape within the body of is_convex are also
replaced by the inherits_from_Shape concept, all of the subtle distinctions
between classes and concepts are mooted, and we don't need to worry that we will create
brittle code that inadvertently assumes that Shape is a class.
We illustrate this ease of writing flexible function bodies with another example
| boolsameShapeMaybeDifferentPosition(Shape auto const&s1, Shape auto const&s2) {   
    Shape const&s1Translated = s1 – s1.lower_left();
    Shape const&s2Translated = s2 – s2.lower_left();
    returnsameShape(s1Translated, s2Translated);
 }
 | 
To see that this code works the same way whether Shape is a class or concept,
let us examine both cases
Shape is a concept
In this case, it means exactly what it does in P1141R1: A concept-constrained function template.
We note particular that, by independent binding, s1 and s2 may
have different types, such as triangle and ellipse.
Shape is a class
On the other hand, if Shape is now a class with virtual functions that
    are implemented by derived classes Triangle and Ellipse,
    the above function still works without changes. In this case, the compiler reinterprets it as
| boolsameShapeMaybeDifferentPosition(inherits_from_Shape auto const&s1, inherits_from_Shape auto const&s2) {   
    inherits_from_Shape const&s1Translated = s1 – s1.lower_left();
    inherits_from_Shape const&s2Translated = s2 – s2.lower_left();
    returnsameShape(s1Translated, s2Translated);
 }
 | 
so if it is called, for example, with two Shape & arguments, instead of
doing compile-time dispatch as it did with the above example, it does runtime dispatch with virtual 
function dispatch.
We note again that because the objects' runtime types may differ, that this correctly corresponds 
to the independent binding of the concepts version above.
Changing Shape between a class and a concept
As we see from the above example, Shape can be changed back and forth
between a class and a concept during tuning and as needs evolve in the future,
replacing a brittle, premature, and commital decision with robust and flexible
code that works equally well with object-orientation and generics.
The role of inferencing
Looking at the body for is_convex at the top of this
paper, we see that the code implicitly assumes that Shape is 
a class in many places. While the above proposal ensures that classes
that may be replaced with concepts later will not be able to use such code,
that may be perceived as too high a price if programming with concepts is
much more difficult than programming with classes. Indeed, irrespective of this proposal,
Bjarne Stroustrup and others have likewise noted that for concepts to reach its full potential,
code written with concepts 
need not be much harder than programming with types and that safe and easy constrained inferencing
is key to that.
So how should the body of is_convex have been written? As the reader
might guess, constrained inferencing as provided by P1141R1 and robust class template argument
deduction are the key.
| boolis_convex(Shape auto const&s) 
 {
   pair p(s, 7); 
   auto x = make_unique<optional>(convex_hull(s)); 
   
 }
 | 
In fact, we would go so far as to say that the code is clearer, more robust, and flexible
than the original class-based code (We understand that contrary examples can be constructed,
but that does not detract from the value of writing code that is agnostic between classes
and concepts).
    Indeed, we would go even further to say that constrained inferencing
as provided by concepts and class template argument deduction is key to writing flexible 
scalable code, one of the most important problems in programming. As has long been known, unconstrained generic code is very flexible
but is difficult to scale as millions of lines of unconstrained types will leave the 
    programmer thoroughly perplexed (this is of course one of the main problems
    concepts was designed to solve). On the other hand, code written with explicit
    types is clear, but also fails to scale as it does not separate an object's implementation
    from the way it is used, resulting in tight coupling where changing the type of
    an object to a different class with the same external behavior can require thousands
    of changes scattered through the code. As a result, large programs become brittle
    and difficult to evolve over time and successive releases can no longer provide rapid
    improvement (or often, any meaningful improvement at all).
We believe that with safe and easy constrained inferencing, loose coupling can be maintained, and writing
code to work for both classes and concepts as described in this paper will result in it being enhanced
    rather than compromised. See P1168R0 for a more
    detailed look with concrete examples at how robust constrained inferencing terse 
    notation and class template argument deduction can enable flexible, readable, and scalable code.
Conclusion
By building on P1141R1 to allow classname auto according to the above rules, writing code that flexibly works with both classes and concepts becomes as
easy as writing concepts code alone, providing a simple unification between the two major programming styles in C++, without the need to
get mired in the myriad subtle differences between generic
and object-oriented programming.