I have inhereted some scala code that extracts data from many different file types. There is a base extractor trait like so:
trait Extractor
{
def startExtract(args:Map[Any,Any) = { .... doExtract(args)}
def doExtract(args:Map[Any,Any) = { .... finishExtract(args)}
def finishExtract(args:Map[Any,Any) = { println(args)} }
}
The Any,Any map is because the various subclasses overload the methods, and may pass along values from start to do that are specific to the subclass. So in ExtractApple, startExtract adds appleColor and doExtract reads it from the map. Horrible, easy to break, hard to test, etc.
My idea is to write it like so:
trait Extractor[T1 <: Args, T2 <: T1, T3 <: T2]
{
def startExtract(args:T1) = { .... doExtract(args)}
def doExtract(args:T2) = { .... finishExtract(args)}
def finishExtract(args:T3) = { println(args)} }
}
So if extractApple needs to add an appleColoe to the args in startExtract, it subclasses args, adds appleColor, then passed that doExtract which just uses args.appleColor, no runtime errors due to not being present or wrong type.
Of course, scala complains about this. StartExtract can’t call doextract because it can’t prove T1 can be used as a T2. When i change the signature of doExtract in a subclass to take argsWithA, it says this is not a valid overload because the type signature has changed.
It seems this must be a somewhat common pattern; is there a way to accomplish this? I know i can abandon the object hierarchy here and rewrite it as a data flow across pure functions, which really is my preference, but i don’t have time to restructure and test this whole code base.
As an aside, the subclasses of extract are never converted to a pure extractor, so converting to a common base class with less specific type parameters isn’t an actual problem.