Search code examples
scalashapeless

Build list of typeclass instances for HList type


I'm trying to improve types for one of our APIs. I want to create data fetcher with following signature:

def runIt[T <: HList](id: Int): T = ???

It should fetch call external server for some entity and return HList with required data. The first task is to build list of API params for required features.

Here it what I made:

import shapeless._
import shapeless.ops.hlist._

trait Feature

trait FeatureFetcher[T] {
  def name: String
}

trait F1 extends Feature
trait F2 extends Feature

implicit case object f1Fetcher extends FeatureFetcher[F1] {
  val name = "f1name"
}

implicit case object f2Fetcher extends FeatureFetcher[F2] {
  val name = "f2name"
}

def makeIt[T <: HList](id: Int)
                      (implicit liftAll: LiftAll[FeatureFetcher, T]) = {

  // I need this, but it does not compile here
  // liftAll.instances.toList.map(_.name).mkString(",")

  liftAll.instances
}

makeIt[F1 :: F2 :: HNil](1).toList.map(_.name).mkString(",")

It actually works. But when I move .toList into makeIt function I get error

Error:(25, 21) could not find implicit value for parameter toTraversableAux: shapeless.ops.hlist.ToTraversable.Aux[liftAll.Out,List,Lub]
liftAll.instances.toList.map(_.name).mkString(",")

How to solve this problem?


Solution

  • Based on segeljakt, try passing liftAll.Out to ToTraversable.Aux via LiftAll.Aux like so

    def makeIt[In <: HList, Out <: HList](id: Int)(implicit
      liftAll: LiftAll.Aux[FeatureFetcher, In, Out],
      toTraversable: ToTraversable.Aux[Out, List, FeatureFetcher[_]]
    ): String = {
      liftAll.instances.toList.map(_.name).mkString(",")
    }
    
    type In = F1 :: F2 :: HNil
    type Out = FeatureFetcher[F1] :: FeatureFetcher[F2] :: HNil
    makeIt[In, Out](1)
    // res1: String = f1name,f2name
    

    or based on ToElmTypes

    case class MakeIt[In <: HList]() {
      def apply[Out <: HList](id: Int)(implicit
        liftAll: LiftAll.Aux[FeatureFetcher, In, Out],
        toTraversable: ToTraversable.Aux[Out, List, FeatureFetcher[_]]
      ): String = {
        liftAll.instances.toList.map(_.name).mkString(",")
      }
    }
    
    MakeIt[F1 :: F2 :: HNil].apply(1)