scalaimplicitshapelesshlist

Given a HList T0::T1:: ... Tn and type R is it possible to infer a function type T0=>T1 ...=> Tn => R?


I want to create something that works like this

implicit class HListOps[AHList<:HList](value:AHList){
    def fold[R](folder: /*What here?*/)={

    }
}

so that it works like this

("HeY"::42::HNil).fold{string=>int=> string+int.toString} // returns "HeY42"

Solution

  • Well, after checking how this problem looks very similar to actually reversing an HList, I used a very similar approach and got this working:

    import java.time.LocalDate
    
    import scala.language.{higherKinds, reflectiveCalls}
    
    import cats.Applicative
    import shapeless._
    
    trait HFolder[InL <: HList, Out] {
      type Fld
      def fold(inL: InL): Function1[Fld, Out]
    }
    
    object HFolder {
    
      implicit def nilHFolder[Out]: HFolder[HNil, Out] {type Fld = Out} = new HFolder[HNil, Out] {
        type Fld = Out
    
        override def fold(inL: HNil): Function1[Out, Out] = identity
      }
    
      implicit def consHFolder[InH, InT <: HList, Out]
      (implicit tailHFolder: HFolder[InT, Out]): HFolder[InH :: InT, Out] {type Fld = InH => tailHFolder.Fld} =
        new HFolder[InH :: InT, Out] {
          override type Fld = InH => tailHFolder.Fld
    
          override def fold(inL: InH :: InT): Function1[InH => tailHFolder.Fld, Out] = {
            folder =>
              inL match {
                case inH :: inT => tailHFolder.fold(inT)(folder(inH))
              }
          }
        }
    
      implicit class HListOps[InL <: HList](inL: InL) {
        def fold[Out](implicit hfolder: HFolder[InL, Out]): Function[hfolder.Fld, Out] = {
          hfolder.fold(inL)
        }
    
      }
    
      //Here compiler can infer correct info (in this case (String=>Int=>LocalDate)=>LocalDate)
      val folder= ("Alejo" :: 29 :: HNil).fold[LocalDate] 
    
    
    }
    

    Only small problem is that after calling the method fold I have to invoke it using the word apply without syntactic sugar because otherwise compiler thinks I'm passing the implicit explicitly.