scalashapelesssingleton-type

Scala shapeless selection from HList of dependent types problem


I have a type dependent Cf type which stores singletoned string:

trait Cf {
  type Name <: String with Singleton
  def value: Name
}
object Cf {

  type Aux[A <: String with Singleton] = Cf { type Name = A }


  def apply(name: String): Aux[name.type] = { 
    val w = name.witness
    new Cf {
      override type Name = w.T
      override def value: Name = w.value
    }
  }
}

Imaging having another class Dbtest which only stores columns of some HList type:

class Dbtest[T <: HList](val columns: T)

Then I wanted to write some extension methods for my Cf type but there is one restiction: type of the instance on which extension method is going to be called have to be presented in HList of Dbtest instance :

object Ops {
  implicit class CfOps[C, N <: String with Singleton, T <: HList](ecf: C)
  (
    implicit 
      db: Dbtest[T]
    , ev: C =:= Cf.Aux[N]
    , selector: Selector[T, C]

  ) {

    private val cf = selector(db.columns)
    def simplePrint(): Unit = println(cf.value)

  }
}

Creating instances of Cf and Dbtest instance with some of them inside:

object Test extends App {
  val c = Cf("c")
  val b = Cf("b")
  val g = Cf("g")
  implicit val db = new Dbtest(c :: b :: HNil)
  ...


I want this to compile because c was specified in the HList:

 c.simplePrint()

And this not to compile because g is not presented in the HList:

g.simplePrint()

Main problem is - I cannot specify implicit Selector type correctly, so my simplePrint() method is not seen by the compiler:

value simplePrint is not a member of Cf{type Name = String("c")}

Is there a way I can specify selector type correctly?


Solution

  • There is no way to infer N in CfOps(ecf) for specific ecf: C, so N is just some abstract type and there is no implicit evidence C =:= Cf.Aux[N].

    Try to replace definition of CfOps with

    implicit class CfOps[N <: String with Singleton, T <: HList](ecf: Cf.Aux[N])(
      implicit
      db: Dbtest[T],
      selector: Selector[T, Cf.Aux[N]]
    ) {
      private val cf = selector(db.columns)
      def simplePrint(): Unit = println(cf.value)
    }
    

    Then

    import Ops._
    c.simplePrint() // compiles
    //g.simplePrint() // doesn't compile