The core ideas that run through all of this work are
- Higher Order Functions
- Internal Iteration
- Typeclasses
None are at all novel.
Higher order functions, functions that take functions as arguments, or return functions, are the central mechanism of Ranges and Senders, as well as being in Stepanov's STL.
Separation of the mechanism, or shape, of an algorithm from the operation the algorithm performs is table stakes these days.
Iteration is also a central concern of the STL.
Applying functions to or inspecting each element in some collection in some manner defined by the structure — abstracted away from the algorithm that applies a user operation — was Stepanov's key insight in defining Generic Programming.
The STL exposes the iteration externally, making Iterators a key component. Algorithms, Iterators, and Containers are the triad that define the STL.
Ranges provides a bridge to internal iteration, where the iteration is not necessarily exposed.
It has turned out that external iteration, using pointers or indices, is a source of needless safety problems.
Moving the details of unsafe operations, such as checking if an iterator is valid, or dereferenceable, into small and reused blocks of code improves safety, and correctness.
It has also turned out that there is little to no loss of performance.
Sometimes even performance improvements because the compiler can see it is lowering known safe code to unsafe implementation and can elide checks it might otherwise need to do.
Compiler middle and back ends have learned an astonishing number of tricks — often driven by other languages that have made safety a priority.
Typeclasses are newer to C++ as an organizing principle, but not as an implementation technique.
They are merely a record that collects a coherent set of named operations together.
In a language with first class support for them the compiler or runtime will arrange to make these names available to functions constrained on them.
I am not proposing a language extension for typeclasses.
I am proposing pure and efficient library mechanisms for discovering and forwarding the bundle of named operations.
Use an object holding callables with well known names, one for each operation in a related family. A concrete interface.
Provide a generic lookup mechanism for the object so that algorithms can find the instance for the types provided to the algorithms. The typeclass instance.
Structs, with named operations. No other required indirection. No required virtual functions. No required inheritance.
Everything visible to the compiler — available for inlining and defunctionalization.
The compiler's middle and back ends are extremely happy with them.
I am proposing some more complicated machinery to help write a typeclass instance, one that derives the rest of the operations from a few core ones.
Consumers of an instance do not care.
All they look for is how to perform one of the named operations the typeclass provides.
How it is provided is not their problem.
Purely duck typing.
The generic programming facilities of C++26 are enough to implement these abstractions type-safely.
Even taking in to account the effects of error handling and failures.
Using these facilities also produces efficient and correct by construction code, at the cost of some debugging overhead without inlining, and some complexity in the implementation of the proposed framework for users to provide typeclass definition for their own types.
Haskell is not the only language to use typeclasses as an organizing principle.
Lean, which the mathematicians and the machine learning people have both taken up lately, borrowed them as well.
A type class describes a collection of overloadable operations. To overload these operations for a new type, an instance is created that contains an implementation of each operation for the new type. For example, a type class named Add describes types that allow addition, and an instance of Add for Nat provides an implementation of addition for Nat.
From Functional Programming in Lean.