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"

+3


source to share


1 answer


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

      

+5


source







All Articles