I want to implement a system that allows me to generically process a type by registering processors for different key paths.
One trait of the system should be composition
, so every processor should extend one common generic protocol Processor
.
Example of usage:
struct Language {
var name = "Swift"
var version = 5.3
}
var processor = TypeProcessor<Language>()
processor.add(procesor: VersionProcessor(), keypath: \.version)
processor.add(procesor: NameProcessor(), keypath: \.name)
var input = Language()
processor.process(value: input)
// Languge version: 5.3
// Languge name: Swift
I've created a playground showing how this can be solved with function composition and then using what we've learned there to recreate your example.
Function composition allows you to create new functions by chaining together existing ones, as long as the types match up.
The
Processor
can be translated to a function that takes an objectO
, transforms it in some way, and returns a new object. Creating the function could be done like this:Now we can compose those two functions. The resulting functions still keeps the signature `(Person) -> Person). We can compose as many functions as we like, creating a processing pipeline.
Moving on to recreating your example. As this answer mentions, the type is generic over the root object. This is just like in the function composition example, and it allows us to group all the processors in an array for example.
When erasing an object, it's important to keep a reference to the original, so that we can use it to implement the required functionality. In this case, we're keeping a reference to its
process(:)
method.Here's two types implementing the
Processor
protocol. Notice the first one has two placeholder types. The second placeholder will get erased.Finally, the
ObjectProcessor
that uses object composition. Notice the array holds object of the same type. An instance of this struct will only be able to processPerson
s for example. What each child processor does is hidden away in the implementation and the fact it may run on different kinds of data does not affect theObjectProcessor
.And here it is in action. Notice I add two processors for the same key.