CRTP Inheritance Question #81
Closed
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
This PR contains three different options that I've managed to get working each of which allow us to reuse some logic from a different class, effectively inheritance but not always as I'll describe below.
It's probably easiest to start with the first commit, understand it, then look at diffs between the subsequent commits to see what changed. The summary is:
I want to be able to create a model for some standard algorithm (Gaussian Processes, Least Squares, ...) but I want those models to be extensible. A good example is probably
LeastSquares.There ^ we have a
LeastSquaresclass which when given features which consist of row vectors in a design matrix it can perform least squares to estimate the coefficients. It uses the CRTPModelBaseclass (see below) to introduce functionality based on whichfit_methods are defined. ThisLeastSquaresclass is useful if you already have the rows of the design matrix, but often that's not the case and you instead need to build the design matrix from something else. TakeLinearRegressionas an example,If that worked (which it doesn't) we could then call
fitwhich would first convert the features, then use theLeastSquaresclass to produce the fit.Question
Now for the question: "How do we best organize CRTP classes to allow for this sort of shared functionality".
Here's a great summary of the problem and a couple options outlied here
Option 1: Cascading Inheritance
If
Ais the base class (ModelBase),Bis the middle class (LeastSquares) andCis the final class ('LinearRegression) then we have can do something like:This approach let's
AinspectBandBinspectCso we can achieve what we need, but there are a few cons. This means thatAis not the sole class in charge of performing CRTP style inspection and extension. See the link above for an example, but it effectively means thatAneeds to deal with all possible implementations ofBandBneeds to deal with all possible implementations ofC. This sort of nested structure would make theLeastSquaresclass difficult to implement and would require redundant (and complicated) code shared between aLeastSquaresclass and aGaussianProcessclass which each wanted to allow for subsequent specialization of which feature types are supported.See commit c89563aab3f5.
Option 2: Transferred Inheritance
In this case we would have:
Note that
Bnow inherits fromA<Derived>notA<B<Derived>>. This is nice because it let's all the CRTP inspection happen insideAwhich now has full access toDerived. The downside is that we loose a bit of safety, havingBinherit fromA<C>is a bit strange. As you can see in the code examples in option 1 I had a safety catch which prevented that sort of (often accidental) inheritance.See commit e0414deec34.
Option 3: No inheritance
Avoid inheritance all together.
Here both
BandCare straight forward CRTP classes. The plus side here is a much simpler structure, the downside is that if we wantCto behave likeBbut with some extra functionality it would need to duplicate all functions available inB. If done right that could happen by calling a bunch of free functions ... but it could still involve quite a bit of boiler plate and make the final step in using these models less approachable.See commit ae408c85579
Option 4
Any ideas?
Model Base
The CRTP base class
ModelBaselooks something like: