My goal is to create a trait that a case class can extend, which can process each constructor argument and then pass them as arguments to a method of the case class. All of the constructor arguments will be the same type with different type parameters, and the method will take arguments that match the type parameter of each constructor argument. You can think of it as a pre-processor of the constructor arguments. For example,
case class Foo(input1:Input[Int], input2:Input[String]) extends MagicTrait {
def run(input1:Int, input2:String) { ... }
}
Is this at all feasible? Is it feasible in a way that isn’t terribly ugly (e.g. all reflection)? Is it possible to refer to the companion object of a case class in a way that is at all generic across case classes (e.g. a function that takes the output of Companion.unapply())?
Seeing as an acceptable solution allows the preprocessing functionality to be moved off the instances to an associated object the main remaining difficulty is that you want to be able to abstract over the arity and types (ie. the shape) of your case class constructors. This is possible with the
HListimplementation and polymorphic function values from shapeless.First some preliminaries,
We can now define a preprocessor base class which provides an apply method which takes an
HListofInputtypes, maps thevaluepolymorphic function across it (ie. performing the preprocessing) and then passes the resultingHListof non-Inputtypes to the provided case class constructor (which is given in hlisted form, see below),Now we define the case class with the post-processing component types,
and add one line of boilerplate,
(here the Foo companion object factory method is provided as the
Preprocessorconstructor argument in HListed form as required above.)Now we can construct
Fooinstances using theFooBuilder.Unfortunately it isn’t (currently) possible to combine the
FooBuilderobject with theFoocompanion object: if you attempt to have theFoocompanion extendPreprocessoryou’ll discover that theFoofactory method isn’t available to be passed as thePreprocessorconstructor argument.To illustrate that this solution is really abstracting over type and arity, here’s how we might add a second differently shaped case class,