0

I want to build a pipeline dependency where 2nd level depends on 1st one, 3rd depends on 2nd AND 1st and so on …

I have defined such structures

trait Level[A <: Level[A]] { 
  type DependsOn <: Level[DependsOn]
  val previousDependencies: List[DependsOn]
}

trait First extends Level[First] { 
  type DependsOn = Nothing 
} 

trait Second extends Level[Second] {
  type DependsOn = First
}
class FirstLevel extends First {
  val previousDependencies = List.empty
}

class SecondLevel(val previousDependencies: List[FirstLevel]) extends Second 

Until now it all works. But I can't get it to work with third structure that depends on two previous ones. I have tried shapeless Product and Coproduct but I can't get it to work properly. I know it has to be a Product meaning sum type, to it implies using shapeless HList. Please help :)

goral
  • 1,275
  • 11
  • 17

1 Answers1

1

You can use Shapeless' heterogenous lists (HLists) together with more custom own-crafted HLists. I'll go with the former, as it'll be cleaner and easier to comprehend. Main idea in code below is that you keep type-chain of your Levels together with "accumulated" type of all element types on the way.

import shapeless.{HNil, HList}

sealed trait Level[MyElement] {
  type Previous <: Level[_]
  type MyList <: HList
  def elements: MyList
}
trait LNil extends Level[Nothing] {
  type MyElement = Nothing
  type MyList = HNil
}
trait LCons[MyElement] <: Level[MyElement] {
  type MyList = shapeless.::[List[MyElement], Previous#MyList]
}

As you can see, MyList holds HList of all element types from levels below. Now we can provide less abstract implementation, allowing easy construction of such Levels:

object ConcreteLNil extends LNil {
  def elements = HNil
}

class ConcreteLCons[MyElement, PreviousList <: Level[_]]
  (thisElems: List[MyElement], val previous: PreviousList) 
    extends LCons[MyElement] {
  type Previous = PreviousList
  type MyElement = String
  def elements = thisElems :: previous.elements
}

You can now use it like that:

val first = new ConcreteLCons(1 :: 2 :: Nil, ConcreteLNil)
val second = new ConcreteLCons("x" :: "y" :: Nil, first)
val third = new ConcreteLCons(1.0 :: Nil, second)

third.elements
// List(1.0) :: List(x, y) :: List(1, 2) :: HNil