Som Bhattacharyya
Som Bhattacharyya

Reputation: 4112

How is an implicit of Generic resolved (using Shapeless guide example)?

I am trying to learn Shapeless following the excellent Shapeless-guide. But while trying out the example i am having several stumbling blocks. One of them is how the Generic[IceCream] is resolved implicitly. I haven't declared a implicit val for an instance of Generic[IceCream] yet it is available. How?

The code looks like this and it compiles just fine,

import shapeless._

case class IceCream(name: String, numCherries: Int, inCone: Boolean)
case class Employee(name: String, number: Int, manager: Boolean)

trait CSVEncoder[A] {
  def encode(value: A): List[String]
}
object CSVEncoder {
  // "Summoner" method
  def apply[A](implicit enc: CSVEncoder[A]): CSVEncoder[A] =
    enc
  // "Constructor" method
  def instance[A](func: A => List[String]): CSVEncoder[A] =
    new CSVEncoder[A] {
      def encode(value: A): List[String] =
        func(value)
    }
}
object AdvancedShapelessUsage extends App {
  //Define all typeclass instances here
  def createEncoder[A](func: A => List[String]): CSVEncoder[A] =
    new CSVEncoder[A] {
      def encode(value: A): List[String] = func(value)
    }
  implicit val stringEncoder: CSVEncoder[String] =
    createEncoder(str => List(str))
  implicit val intEncoder: CSVEncoder[Int] =
    createEncoder(num => List(num.toString))
  implicit val booleanEncoder: CSVEncoder[Boolean] =
    createEncoder(bool => List(if (bool) "yes" else "no"))
  implicit val hnilEncoder: CSVEncoder[HNil] =
    createEncoder(hnil => Nil)
  implicit def hlistEncoder[H, T <: HList](implicit hEncoder: CSVEncoder[H], tEncoder: CSVEncoder[T]): CSVEncoder[H :: T] =
    createEncoder {
      case h :: t =>
        hEncoder.encode(h) ++ tEncoder.encode(t)
    }


  implicit def genericEncoder[A, R]( //Use the Aux pattern to take out the `Repr` type 
    implicit gen: Generic[A] {
      type Repr = R
    },
    enc: CSVEncoder[R]): CSVEncoder[A] = createEncoder(a => enc.encode(gen.to(a)))

  val iceCreams: List[IceCream] = List(
    IceCream("Sundae", 1, false),
    IceCream("Cornetto", 0, true),
    IceCream("Banana Split", 0, false))

  def writeCsv[A](values: List[A])(implicit enc: CSVEncoder[A]): String =
    values.map(value => enc.encode(value).mkString(",")).mkString("\n")

  writeCsv(iceCreams)

}

When we say implicit gen: Generic[A] { type Repr = R }

i figure the compiler will look for a Generic[IceCream] and then refer and assign the Repr to the R type. I cannot understand how without any Generic[IceCream] being available is it resolved. So magic I am not understanding I guess.

Upvotes: 0

Views: 140

Answers (1)

Edmondo
Edmondo

Reputation: 20090

If I understood your question correctly, you wonder how that Generic[IceCream] can be resolved. It can be resolved for two reasons:

  1. The implicit scope for any trait T includes its companion object T.
  2. Shapeless has a macro which generates a Generic[T]: this use case is the common usage of macros named "implicit materializer"

Upvotes: 1

Related Questions