I have done a few implementations of HList now. One based on Daniel Spiewak\'s High Wizardry in the Land of Scala talk and another based on a post in Apocalisp blog. The goal
Note that you have an example of Map with HList in the recent (October 2016, 5 years after the OP) article "Using shapeless' HLists for extra type safety (in Akka Streams)" from Mikołaj Koziarkiewicz.
//glue for the ParserStageDefs
specs.map(s => Flow[Data].map(s.parser).map(s.processor))
.foreach(broadcast ~> _ ~> merge)
The problem lies in the fact that the type information in our specs list is not preserved. Or rather, not preserved the way we want to - the type of the
Listelements isParserStageDef[_ >: Int with String], so the lowest common supertype for our decorator and incrementer.The above implies that, when mapping between the parser and processor elements, the compiler has no way to provide the actual type
Tthat's used within the given spec.A solution
Here's where HLists come to the rescue. Because they preserve the complete type information for each element, it's possible to define our flow very similarly to our last attempt.
First, let's replace our list with an
HList:
import shapeless.ops.hlist._
import shapeless._
//...
val specs = decorator :: incrementer :: HNil
val specsSize = specs.length.toInt
Now, for the mapping from
ParserStageDefsintoFlows, we need to take a different approach, as themapforHListrequires something called P**oly - a polymorphic function value**.Here's how one would look like in our case:
import shapeless.PolyDefns.~>
object toFlow extends (ParserStageDef ~> ProcessingFlow) {
override def apply[T](f: ParserStageDef[T]) =
Flow[Data].map(f.parser).map(f.processor)
}
For it to work, we'll also have change
ProcessingFlowto typeProcessingFlow[_] = Flow[Data, Data, _], since the polymorphic function above expects a higher-kinded type.Now, our central statement turns out to be:
//we convert to a List[ProcessingFlow[_]] for simplicity
specs.map(toFlow).toList.foreach(broadcast ~> _ ~> merge)
and we're all set!