ntn
ntn

Reputation: 1177

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]

Upvotes: 8

Views: 683

Answers (2)

Michael Zuber
Michael Zuber

Reputation: 36

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
}

Upvotes: 2

flavian
flavian

Reputation: 28511

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"
)

Upvotes: 1

Related Questions