Trying to figure out some type ceremony in one of Doobie examples

#1

Hi all,

Reading Doobie examples (the Orm.scala example) I don’t understand this Dao initialization snippet (I simplified it here to focus on the question):

  trait Dao[A] {
    type Key
    // some methods
  }
  object Dao {

    type Aux[A, K] = Dao[A] { type Key = K }

    def apply[A](implicit ev: Dao[A]): Aux[A, ev.Key] = ev

    object derive {
      def apply[A, K] = new Partial[A, K]
      class Partial[A, K] {
        def apply[R <: HList, S <: HList](table: String, keyCol: String)(
          implicit // some implicits that facilitate serialization/deserialization to/from JDBC
        ): Aux[A, K] =
          new Dao[A] {
            type Key = K

           // implementation methods
         }
  }

and later:

  // a little usage example

  final case class Neighbor(name: String, age: Int)
  object Neighbor {
    implicit val dao: Dao.Aux[Neighbor, Int] =
      Dao.derive[Neighbor, Int]("neighbor", "id")

    implicit val show: Show[Neighbor] = Show.fromToString
  }

  val prog: ConnectionIO[String] = {
    val dn = Dao[Neighbor]
    import dn._
    // rest of the program
  }

I feel like this is a very elaborate way to say what amounts to new Dao[Neighbor] but I am certain that I am missing something. What would that be? Is there a reason for this pattern, maybe even a name for it?

Thanks.

#2

I can’t find that example anywhere on the doobie homepage, so I have to guess, but if your question is why Aux and derive are needed, I suspect that they are required for resolving the implicits in Partial.apply.

This blog entry about the Aux pattern explains, what it does. It’s required for type level computations, but I’m not familiar enough with doobie to explain what it is used for here.

#3

You can find the full example here: https://github.com/tpolecat/doobie/blob/series/0.8.x/modules/example/src/main/scala/example/Orm.scala.

Thanks for the link, it seems to be what I am after.