Implicit evidence from a type within a trait

2019-08-17 04:48发布

问题:

Let's say we have the following:

object MyValue {
  case class MyBoolean(record: Boolean) extends MyValue
  case class MyDouble(record: Double) extends MyValue
}

trait MyValue

and a sealed trait:

object MyValueGrabber {
  trait MyBooleanGrabber[T] extends MyValueGrabber[T] {
    override def apply(record: T): Option[MyValue.MyBoolean]
  }

  trait MyDoubleGrabber[T] extends MyValueGrabber[T] {
    override def apply(record: T): Option[MyValue.MyDouble]
  }
}

sealed trait MyValueGrabber[T] {
  def apply(record: T): Option[MyValue]
}

as well as a mapping between the MyValue type and a different type with which it is associated:

object Mapper {
  implicit val myBooleanMapper = new Mapper[MyValueGrabber.MyBooleanGrabber[_], String] {
    override def getValue: String = "found a bool"
  }

  implicit val myDoubleMapper = new Mapper[MyValueGrabber.MyDoubleGrabber[_], Long] {
    override def getValue: Long = 100L
  }
}

trait Mapper[A, B] {
  def getValue: B
}

and then we have:

trait HolyGrail[T] {
  def name: String
  def myValueGrabber: MyValueGrabber[T]
  def getValue[U] = ???
}

The question is how do we use the Mapper implicitly to return the correct type U. Ideally, it would be something like:

def getValue[U](implicit ev: Mapper[myValueGrabber.type, U]) = ev.getValue  // doesn't work

回答1:

You should use it like this:

def getValue[U](implicit ev: Mapper[MyValueGrabber[T], U]) = ev.getValue

As methods do not have .type. (Their converted function have, but I think that is not what you want.)

Based on the clarifications in the comments, I think you want something like Shapeless' HMap.

You will probably need to adjust your ValueGrabber trait a bit:

sealed trait MyValueGrabber[T, -R <: MyValue] {
  def apply(record: T): Option[R]
}

or

sealed trait MyValueGrabber[T] {
  type R <: MyValue
  def apply(record: T): Option[R]
}

probably the latter is harder to use in your getValue:

def getValue[U, O](implicit ev: Mapper[MyValueGrabber[T] {type R = O}, U]): U = ev.getValue

the former would look like this (but as I understand not what you want):

trait HolyGrail[T, R] {
  def name: String
  def myValueGrabber: MyValueGrabber[T, R]
  def getValue[U](implicit ev: Mapper[MyValueGrabber[T, R], U]): U = ev.getValue
}

I do not know how to do the thing you want (path dependent type with trait) without shapeless.



标签: scala