| > The most obvious examples are ML and its derivatives. I'm reasonably familiar with F# but I know that's strayed quite far from its ML roots and its generics are constrained to some degree by what the CLI supports. For the kinds of generic programming that C++ does well it's not clear to me where the ML style is superior. Can you give some specifics? > If C++ had an actual notion of abstract types, then templates could be type-checked against ADT specifications (so-called “concepts”), rather than having to wait until instantiation time. Yep, that's why everyone wants Concepts to be standardized. > You can't encapsulate a single abstraction providing two or more ADTs without using `friend`. That's a fact. I'm still not clear what the issue is here. A C++ class can implement multiple interfaces for dynamic polymorphism without using friend and can implement more than one 'concept' for static polymorphism without using friend. Can you explain what you mean in more detail? > This is a reply to the wrong thing. Concepts are ADT specifications, not object specifications. Are you talking about something like Go interfaces? For my use cases static polymorphism is generally preferable to dynamic polymorphism and in those cases where dynamic polymorphism is desirable C++ style interfaces are often sufficient. When the need arises for something like a Go interface in C++ people usually use type erasure but implementing that currently requires a bit more boilerplate than would be ideal (though there are libraries that help). That's something that could be solved in the future by something like Herb Sutter's metaclass proposal. Perhaps I'm still not understanding your point though. |
Concepts are what ML and Haskell have been calling “signatures” and “type classes”, respectively, for ages. Unlike concepts, which only exist in the collective minds of C++ programmers, signatures and type classes are actual features of existing type systems, so, for instance, the type checker can make sure that you aren't trying to use an inexistent (member) function - without ever attempting to instantiate your generic code.
It is very unfortunate that F# got rid of this important feature.
> Yep, that's why everyone wants Concepts to be standardized.
What I'm telling you is other languages have had this very same feature for well over two decades.
> I'm still not clear what the issue is here.
Here's a thought exercise: Design an API for manipulating graphs, nodes and edges. The concrete representation of these three types must be hidden from the user by language-enforced mechanisms. Using `friend` is not allowed. Using the pimpl pattern is not allowed. Bypassing type safety is not allowed.
The fundamental problem that you'll run into is that C++'s access levels work with one type at a time. ML modules don't have this problem, because a single signature can specify multiple abstract types. An implementation can have access to the representations of all three types (graph, node, edge), while at the same time hiding these representations from all clients.
> Are you talking about something like Go interfaces?
Yes.
> For my use cases static polymorphism is generally preferable to dynamic polymorphism and in those cases where dynamic polymorphism is desirable C++ style interfaces are often sufficient.
I agree: static polymorphism is preferable whenever possible. It is easier to reason about both for language users (who care about correctness) and compiler writers (who care about optimizations). However, sometimes you want dynamic polymorphism, and that's what objects (in the object-orientation sense, which you can think of as “thingies that have vtables”) are for.