Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to get evidence that each type element in Shapeless HList belongs to a typeclass

I've tried the following:

trait Evidence[H <: HList, T[_]] {}

object Evidence {
  implicit def HNilEvidence[T[_]] = new Evidence[HNil, T] {}
  implicit def HListEvidence[Head, Remaining <: HList, T[_]](implicit headEv: T[Head], remainingEv: Evidence[Remaining, T]) =
    new Evidence[Head :: Remaining, T] {}
}

This correctly gives me an implicit evidence, only when all elements match a typeclass.

But, when trying to use it like this (a contrived example):

def transform[A](a: A)(implicit ev: Evidence[A :: HNil, Ordering]) =
   { List(a,a).sorted }

this gives the error

error: No implicit Ordering defined for A.

which should be there based on presence of Evidence[A :: HNil, Ordering]

like image 816
ntn Avatar asked Nov 10 '15 04:11

ntn


2 Answers

You don't need to define your own Evidence type class. Use shapeless' ops.hlist.LiftAll.

def transform[A](a: A)(implicit
                                     liftAll: LiftAll.Aux[Ordering, A :: HNil, Ordering[A] :: HNil],
                                     isHCons: IsHCons.Aux[Ordering[A] :: HNil, Ordering[A], HNil]): List[A] = {
  implicit val ordering: Ordering[A] = liftAll.instances.head
  List(a, a).sorted
}
like image 83
Michael Zuber Avatar answered Oct 17 '22 13:10

Michael Zuber


Why not express the contents of the List with an LUB and simply move the request for ordering evidence at the method level? The typeclass bit here is misleading, what you are matching seems to be a type unless I'm missing the obvious, not a type-class or a type-family.

LUBConstraint already exists in Shapeless for this reason.

class Transformer[HL <: HList, T](list: T :: HL)(
  implicit val c: LUBConstraint[HL, T]
) {
  def transform(obj: List[T])(
    implicit ev: Ordering[T],
    toList: ToList[T :: HL, T]
  ): List[T] = (toList(list) ::: obj).sorted(ev.reverse)
}

I also don't really understand why you need the A type param and what the relation is between A and the LUB. I've added an example for you just to make a point, hopefully it's useful enough to show you how to combine things.

val list = "test3" :: "test1" :: "test2" :: HNil
val listb = List("test5", "test4", "test6")
val transformer = new Transformer(list)

transformer.transform(listb) shouldEqual List(
  "test6",
  "test5",
  "test4",
  "test3",
  "test2",
  "test1"
)
like image 41
flavian Avatar answered Oct 17 '22 12:10

flavian