How to create a typeclass with existential HList element
I am trying to create a personalized style type that mimics the shapeless types. It looks like this:
trait View[Record] {
type Result <: HList
def apply(r: Record): Result
}
object View extends LowPriorityLiftFunction1{
type Aux[Record, L <: HList] = View[Record] {type Result = L}
implicit def atView[Record: View] = at[Record](implicitly[View[Record]].apply)
}
Suppose I provide it with this functionality:
object toHView extends ->( (_:Int) + 1)
implicit def provideView[Record, L <: HList]
(implicit generic: Generic.Aux[Record, L],
mapper: Mapper[toHView.type, L])
: View.Aux[Record, mapper.Out] =
new View[Record] {
type Result = mapper.Out
def apply(r: Record) = mapper(generic.to(r))
}
So, if we define:
case class Viewable(x: Int, y: Int, z : Int)
case class NotViewable(x: Int, y: Long, z : Int)
then
val view = View(Viewable(1, 2, 3)) // is 2 :: 3 :: 4 :: HNil
val noView = View(NotViewable(1, 2, 3)) // is HNil
The problem is here if I try to acquire
view.head
I have
Error: Could not find implicit parameter value
c: IsHCons[View[Viewable]#Result]
How can I define this class of classes to efficiently use all of its types later on?
Of course, I could get rid of members like:
trait View[Record, Result <: HList] {
def apply(r: Record): Result
}
object View extends LowPriorityLiftFunction1{
implicit def atView[Record, Result]
(implicit view: View[Record, Result]) = at[Record](view.apply)
}
object toHView extends ->((_: Int) + 1)
implicit def provideView[Record, L <: HList]
(implicit generic: Generic.Aux[Record, L],
mapper: Mapper[toHView.type, L])
: View[Record, mapper.Out] =
new View[Record, mapper.Out] {
type Result = mapper.Out
def apply(r: Record) = mapper(generic.to(r))
}
but from this point in
val view = View(Viewable(1, 2, 3))
I am getting "ambiguous implicit values"
source to share
Ok, here it is: change
implicit def atView[Record: View] = at[Record](implicitly[View[Record]].apply)
to
implicit def atView[Record](implicit v: View[Record]) = at[Record](v.apply(_))
The reason is that it implicitly
loses precision when dealing with qualified type members, so instead of the expected qualified type of yours HList
(which in this case would be Int :: Int :: Int :: HNil
), the compiler splashes out rather useless View#Result
.
Using an implicit parameter instead of binding to a context seems to preserve the qualified type.
Also, shapeless' the
is an alternative implicitly
that preserves type qualifications, although it doesn't seem to work in this case .
Here is an example of the implicitly
loss of precision taken from the the
implementation in the shapeless :
scala> trait Foo { type T ; val t: T }
defined trait Foo
scala> implicit val intFoo: Foo { type T = Int } = new Foo { type T = Int ; val t = 23 }
intFoo: Foo{type T = Int} = \$anon\$1@6067b682
scala> implicitly[Foo].t // implicitly loses precision
res0: Foo#T = 23
scala> implicitly[Foo].t+13
<console>:13: error: type mismatch;
found : Int(13)
required: String
implicitly[Foo].t+13
^
scala> the[Foo].t // the retains it
res1: Int = 23
scala> the[Foo].t+13
res2: Int = 36
source to share