Search code examples
scalashapeless

Shapeless: holding Prepend and Split for delayed execution


My goal is to Wrap an HList and save enough information to perform prepend and split operations at a later time.

case class Wrap[L <: HList](wrapped: L)

val x = Wrap("x" :: "y" :: HNil)
val y = Wrap(1 :: 2 :: HNil)

case class Append[L1, L2](w1: Wrap[L1], w2: Wrap[L2], prepend: Prepend[L1, L2], length: Length[L1])

def append[L1, L2](w1: Wrap[L1], w2: Wrap[L2])(implicit prepend: Prepend[L1, L2], length: Length[L1]) = Append(w1, w2, prepend, length)

val xAppendY = append(x,y)

val merged = xAppendY.prepend(xAppendY.w1.wrapped, xAppendY.w2.wrapped)

val split = Split[xAppendY.prepend.Out, xAppendY.length.Out] // <-- error here

split.apply(merged)

This code fails with the implicit not found error:

Implicit not found: shapeless.Ops.Split[xAppendY.prepend.Out, xAppendY.length.Out]. You requested to split at position xAppendY.length.Out, but the HList xAppendY.prepend.Out is too short.

But it seems the the compiler should know that the types are String :: String :: String :: String :: HNil and Nat._2. Is there something that I need to do to help the compiler out here?


Solution

  • The following version of code works:

      import shapeless.ops.hlist.{Length, Prepend, Split}
      import shapeless.{::, HList, HNil, Nat}
    
      case class Wrap[L <: HList](wrapped: L)
    
      val x = Wrap("x" :: "y" :: HNil)
      val y = Wrap(1 :: 2 :: HNil)
    
      case class Append[L1 <: HList, L2 <: HList, L3 <: HList, N <: Nat](w1: Wrap[L1], w2: Wrap[L2], prepend: Prepend.Aux[L1, L2, L3], length: Length.Aux[L1, N])
    
      def append[L1 <: HList, L2 <: HList, L3 <: HList, N <: Nat](w1: Wrap[L1], w2: Wrap[L2])(implicit prepend: Prepend.Aux[L1, L2, L3], length: Length.Aux[L1, N]) = Append(w1, w2, prepend, length)
    
      val xAppendY = append(x,y)
    
      val merged = xAppendY.prepend(xAppendY.w1.wrapped, xAppendY.w2.wrapped)
    
      val split = Split[xAppendY.prepend.Out, xAppendY.length.Out]
    
      split.apply(merged)
    

    In your version of code xAppendY.prepend was of type Prepend[L1, L2] = Prepend[L1, L2] { type Out } rather than Prepend.Aux[L1, L2, L3] = Prepend[L1, L2] { type Out = L3 } for proper L3 and xAppendY.length was of type Length[L1] = Length[L1] { type Out } rather than Length.Aux[L1, N] = Length[L1] { type Out = N } for proper N.

    Why do we need to specify a refined type (or its equivalent Aux) for the output of certain type computations?