scalashapelessimplicitscircescala-2.12

Folding over provided HList


I am aware that my problem might be "XY problem", so here is a brief recap of what i want to achive. Let's say i have the following type:

trait Provider[T] { def provide: T }

I want to be able to combine several values provided from that type into one ( each provided can have different type). Combination happens in different part of the program, let's define type Combiner (tried to apply shapeless to my problem):

trait Combiner[Out, Providers <: HList] { 
  def combine(providers: Providers): Out 
}

I came up with the following:

class ShapelessCombiner[Out, Providers <: HList, P](implicit
  zero: Out,
  folder: LeftFolder.Aux[Providers, Out, P, Out]
) extends Combiner[Out, Providers] {
  def combine(providers: Providers): Out = folder(folder, zero)
}

In order to specify what function is used for folding i created helper method:

def shapelessCombiner[Out, Providers <: HList](
  fun: Poly
)(implicit
  zero: Out,
  folder: LeftFolder.Aux[Providers, Out, fun.type, Out]
) = new ShapelessCombiner[Out, Providers]

All nice, except it doesn't work. I was trying to test it, using the following code:

implicit val zero = Json.obj()

//incorrect implementation, but compilation is important here
object poly extends Poly2 {
implicit def caseInt =
    at[Json, Provider[Int]]((j, p) => j deepMerge Json.fromInt(p.provide))
  implicit def caseString =
    at[Json, Provider[String]]((j, p) => j deepMerge Json.fromString(p.provide))
}
def hlistInstance: Provider[Int] :: Provider[String] :: HNil = ???
val directResult = hlistInstance.foldLeft(zero)(poly) //compiles
val combiner = shapelessCombiner[Json, Provider[Int] :: Provider[String] :: HNil](poly)//do not compile, LeftFolder.AUX not found

My question is:

a) How can i solve this compilation issue, or

b) Is there different/better way to solve my initial problem

Edit

It turns out that my error was caused by wrong imports, i somehow imported tuple.LeftFolder instead of hlist.LeftFolder, silly mistake


Solution

  • The following code compiles

      import io.circe.Json
      import shapeless.{::, HList, HNil, Poly, Poly2}
      import shapeless.ops.hlist.LeftFolder
    
      trait Provider[T] {
        def provide: T
      }
    
      trait Combiner[Out, Providers <: HList] {
        def combine(providers: Providers): Out
      }
    
      class ShapelessCombiner[Out, Providers <: HList, P](implicit
                                                          zero: Out,
                                                          folder: LeftFolder.Aux[Providers, Out, P, Out]
                                                         ) extends Combiner[Out, Providers] {
        def combine(providers: Providers): Out = folder(providers, zero)
      }
    
      def shapelessCombiner[Out, Providers <: HList](
                                                      fun: Poly
                                                    )(implicit
                                                      zero: Out,
                                                      folder: LeftFolder.Aux[Providers, Out, fun.type, Out]
                                                    ) = new ShapelessCombiner[Out, Providers, fun.type]
    
      implicit val zero = Json.obj()
    
      object poly extends Poly2 {
        implicit def caseInt =
          at[Json, Provider[Int]]((j, p) => j deepMerge Json.fromInt(p.provide))
        implicit def caseString =
          at[Json, Provider[String]]((j, p) => j deepMerge Json.fromString(p.provide))
      }
      def hlistInstance: Provider[Int] :: Provider[String] :: HNil = ???
      val directResult = hlistInstance.foldLeft(zero)(poly)
      val combiner = shapelessCombiner[Json, Provider[Int] :: Provider[String] :: HNil](poly)